首页 > 代码库 > 8.12联考题解

8.12联考题解

       所谓今天的题格外水,dalao们纷纷AK……但是本蒟蒻并没有看出来有多水啊……虽然还是水过了一道题,然后剩下的题就照常地转不过来弯,分数和往常并没有多大变化= =。

 

灌水

时间限制:1s 空间限制:256MB

技术分享

样例输入1:

3 1

样例输出1:

3 1 2

样例输入2:

4 1

样例输出2:

4 3 1 2

样例输入3:

8 17

样例输出3:

6 2 3 1 8 4 5 7

技术分享

 

题解

      这个题,第一眼看过去很interesting,第二眼看过去一脸茫然……头一遍读题的时候看到数据范围里那句“答案为-1”觉得很逗,这么说这题是不会爆零啦?然后左右两边用极大值拦住,高考知识等差数列算极大值,把几种特殊情况处理完。然后呢,然后我就不会做了,完全没有思路,唯一能想到的就是dfs……一直到交卷也只想到了dfs;当然这题是一定有简单解法的,但是不管是dp还是比dfs高级一点的其他算法都想不出来。最近发现一个问题:当有一道题我连大体方向都想不出来的时候,它多半是个贪心……

       刚开始处理的极大值当然是把n和n-1放在两端,而dfs的暴力求解也是求每个点两边的最大值,但是没有想到可以移动n和n-1来贪心。想想从最大值慢慢变小的过程:要变小1,把n-2扔到外面去;要变小2,把n-3扔到外面去,以此类推。外面的木条可以递减排列,一点水都存不住。这样通过把n和n-1里面的木条有选择地扔出去,就可以得到从0到最大值的任意解了。考试的时候没有想明白这一点,贪心自然也无从谈起;从1到n-2枚举木条,如果当前多余的水量大于等于它上方的水量就把它放出去,直到n和n-1之间正好是需要的水量,同时使外面没有水,O(n)的效率对这题来说绰绰有余。

       这道题A掉的人很多,我却没有想到。主要原因是在考场上思路不够宽阔(大概经历了这一回对贪心会更敏感些吧),囿于思维定式总是想着用dp一类的做法解决问题。还有一个很重要的问题是从题目中总结归纳结论的能力不够强。结论是题目本身的性质,也就是解题的钥匙;先分析结论要比挖空心思想算法重要得多,也有用得多。

 

技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
const int sj=1000005;
int ans[sj],a1,a2,wm[sj];
long long m,ma,n;
int main()
{
    scanf("%lld%lld",&n,&m);
    ma=((n-1)*(n-2))>>1;
    if(ma<m)
    {
       printf("-1");
       return 0;
    }
    if(ma==m)
    {
       printf("%d ",n-1);
       for(int i=1;i<=n-2;i++)
         printf("%d ",i);
       printf("%d\n",n);
       return 0;
    }
    ans[++a1]=n;
    ma=0;
    for(int i=1;i<=n-2;i++)
    {
      if(ma+(n-1-i)<=m) 
      {
         ma+=n-1-i;
         ans[++a1]=i;
      }
      else  wm[++a2]=i;
    }
    for(int i=1;i<=a1;i++)
      printf("%d ",ans[i]);
    printf("%d ",n-1);
    for(int i=a2;i>1;i--)
      printf("%d ",wm[i]);
    printf("%d\n",wm[1]);
    return 0;
}
water

 

 

序列

时间限制:1s 空间限制:256MB

技术分享

样例输入:

5 3

2 4 2 3 4

样例输出:

39

技术分享

 

题解

      这道题比上一道还要简单……考场上有两道题A得易如反掌无怪乎dalao们都去致力于AK了。第一遍读题,这东西不是和昨天的《区间第K大》,大前天的《calc》完全一个套路吗?!昨天虽然没想出区间第k大,今天总不能指望我再想不到“区间第一大”了吧。整个序列一共有m个比某数小的数,它作为长度为k的子序列第一大的情况数不就是从m个数里选k-1个的组合数吗?吓得我赶紧回忆了一下ad爷讲过的那些求组合数的方法,什么Lucas中国剩余定理还是啥反正我都不会,一看数据范围50*10^5,这个东西递推过去毫无压力啊……要说有多少个比它小的数,sort一遍不就全知道了~有取模,也用不着挣扎高精。那就打?五分钟的功夫打了二十几行的小代码过了样例,虽然理论上完全没有问题,还是觉得开考十几分钟A题有点虚啊;然后开始写dfs对拍,造了几个大数据正解秒过,dfs慢到不能忍,于是换小数据拍来拍去一直没什么问题。事实证明这题就是这么水,五分钟的代码最后真的过掉了。所以说今天是真的NOIP模拟吗……联赛也没有这么简单啊。或许这道题对于集训之前的我来说还是比较绕的,但是经过了这几天的考试对于什么“区间”、“序列”、“子序列”、“第K大”之类的字眼极其熟悉了,要是在过去大概只能想到暴力枚举吧。

 

技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int sj=100005;
const int mod=1000000007;
long long c[sj][52],a[sj],ans;
int n,k;
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {  
      scanf("%lld",&a[i]);
      c[i][0]=1;
    }
    sort(a+1,a+n+1);
    c[0][0]=c[1][1]=1;
    for(int i=1;i<=n;i++)
      for(int j=1;j<=k&&j<=i;j++)
        c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
    for(int i=1;i<=n;i++)
      ans=(ans+(a[i]*c[i-1][k-1])%mod)%mod;
    printf("%lld",ans);
    return 0;
}
seq

 

 

改造二叉树

时间限制:1s 空间限制:128MB

技术分享

样例输入:

3

2 2 2

1 0

1 1

样例输出:

2

技术分享

 

题解

       第一眼看到数论吓了一跳,后来仔细一看是树论……发现自己现在根本分不清“数”和“树”这两个字,写演草的时候也常常写串。带着昨天《公主的朋友》满树烂苹果的心理阴影,前五分钟在想树归。开了个头发现不大对劲,想一想目标是一棵二叉搜索树,而且树的形态和位置都是不变的(没有merge!没有leftturn!没有splay!),那么它的中序遍历也是确定的,目的就是得到一个递增的中序遍历。于是乎成功下树。然后我眼前浮现了一大片玉米,还有方伯伯超伯伯和他的树状数组优化dp,或者某整修道路的zyf大佬的离散dp……但是首先,这道题n^2的效率过不了;其次,不能取等这事使我很头疼,不离散明显没法做,离散了还是不知道应该怎么做。最后直接打了个不知所云的暴力dp,得了点聊胜于无的分数。

      这道题又是超伯伯超哥讲的%%%。我在今天才第一次回想起动归的第一课,莫名也是我最差的一课:线性dp。所有点的权值都可以任意改变,却完全没想到"最长不下降子序列"这个事,感觉这个词已经非常陌生了。至于不能取等的事,有一个非常神奇的技巧可以解决:把a1,a2,a3,a4……an映射到a1-1,a2-2,a3-3,a4-4……an-n,就可以把严格递增整数序列整合为非严格递增整数序列,对处理后的a[i]进行离散之后LIS就可以放开跑了。还有一个问题,LIS是n^2的,这题过不了,可以记录一下每个长度的最小a[i],方便快速进行转移。这些基础dp的优化很长时间没有看过,早就忘得差不多了,现在做起来未必比状压打得熟。这道题对于我来说整合序列和优化LIS都是盲点,想要解决问题确实很困难;不过做了这道题get到很多新的技能,也算是有所收获吧。

 

技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int sj=100005;
int n,a[sj],b[sj],a1,a2,cnt,f[sj];
struct tree
{
     int l,r;
}t[sj];
void zx(int x)
{
     if(t[x].l) zx(t[x].l);
     b[++cnt]=a[x];
     if(t[x].r) zx(t[x].r);
}
struct ls
{
     int a,num;
}l[sj];
int comp(const ls&x,const ls&y)
{
    return x.a<y.a;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) 
      scanf("%d",&a[i]);
    for(int i=2;i<=n;i++)
    {
       scanf("%d%d",&a1,&a2);
       if(a2) t[a1].r=i;
       else   t[a1].l=i;
    }
    zx(1);
    for(int i=1;i<=n;i++) a[i]=b[i]-i; 
    for(int i=1;i<=n;i++)
    {
       l[i].num=i;
       l[i].a=a[i];
       f[i]=1;
    }
    sort(l+1,l+n+1,comp);
    a1=0,a2=1;
    for(int i=1;i<=n;i++)
    {
       if(l[i].a>l[i-1].a) a1++;
       a[l[i].num]=a1;
    }
    memset(b,0x7f,sizeof(b));
    for(int i=1;i<=n;i++)
    {
       for(int j=1;j<=a2;j++)
         if(b[j]<=a[i]) 
            f[i]=j+1;
       if(a[i]<b[f[i]])    b[f[i]]=a[i];
       if(f[i]>a2)         a2=f[i];
    }
    printf("%d",n-a2);
    return 0;
}
binary

 

 

 

       做点反思,倒不是关于成绩,而是考试状态的问题。今天考试的后半段莫名感觉很无聊,T2稳得很,T1一筹莫展,T3虽然刚开始想到中序遍历的时候充满了希望但是后来就不知道应该怎么办了。在演草纸上写来写去,到后来就有点不知道自己在干什么的感觉……假期集训已经只剩下最后几天了,考试题在迷之难度和NOIP模拟之间随机掉落,还是有很多题解决不了。今天下午倒是复习了二分图做了好几个不同类型的图论题,但是内网上剩下的题都完全不知道是在说什么。有题做不出来其实是个挺正常的事,但是最重要的是做不出来题的时候状态怎样。有时候想着想着思路就飘了,想到的方法越来越不靠谱,极其颓废。珍惜能完全学奥赛的日子,给这个夏天画一个圆满的句号。

 

8.12联考题解