zoukankan      html  css  js  c++  java
  • 题解 Educational Codeforces Round 80 [Rated for Div. 2](CF1288)

    前言:11点的时候某天下第一可爱的萌神问我怎么不打CF,跑去开题,11:30终于开了C和D(我家网速令人头秃),秒了一下,考后萌神差点阿克并告诉我E的tag于是我赛后补题。

    A:n/x上取整是(n-1)/x+1,式子变形成x+1+(n-1)/(x+1)<=d。根据a+b>=2√ab随便化简一下。(20s秒了??)

     1 #include<stdio.h>
     2 #include<math.h>
     3 using namespace std;
     4 int T,n,d,x,y;
     5 int main(){ 
     6     scanf("%d",&T);
     7     while(T--)
     8         scanf("%d%d",&d,&n),x=sqrt(n-1),y=x-1,((n<=d)||(x+1+(n-1)/(x+1)<=d)||(y+1+(n-1)/(y+1)<=d))?puts("YES"):puts("NO");
     9     return 0;
    10 }
    View Code

    B:a*b+a+b=a*10b的位数+b。化简得知b+1=10b的位数。所以只有9,99,999……这样是可行的。那么统计B是几位数啥的随便算算再乘个A输出,还有要判相等。(1min不到秒了??)

     1 #include<stdio.h>
     2 const int x[]={0,0,9,99,999,9999,99999,999999,9999999,99999999,999999999,1000000001};
     3 const int y[]={0,0,1,2,3,4,5,6,7,8,9};
     4 int T,a,b,i;
     5 int main(){  
     6     scanf("%d",&T);
     7     while(T--){
     8         scanf("%d%d",&a,&b);
     9         for(i=1;i<=10&&x[i+1]<=b;++i);
    10         printf("%lld
    ",1ll*a*y[i]);
    11     }
    12     return 0;
    13 }
    View Code

    C:做过构造解的原题呢。会两种dp的写法。(我似乎看了10s就会了O(n2m)的做法,想了10min才想到O(nm)的。。还是太菜了

    由于A不降B不升,而且ai<=bi,所以b最小的比a最大的大,那我们可以把b翻转过来并起来看,于是这就是要我们构造一个长为2*m的单增序列。

    一眼秒的O(n2m)做法:设f[i][j]=到i,选择的元素为j的方案。那么f[i][j]+=f[i-1][k](k<=j)。ans=Σf[2*m][i],1<=i<=n

    再想想的O(nm)做法:f[i][j]+=f[i-1][j]+f[i][j-1]。就是把枚举k的那一维省去,直接把j的贡献用j-1的贡献计算,这里利用前缀和的思想,就相当于j-1已经存了之前的答案了,可以直接转移到j。 ans=f[2*m][n]。

    upd:经评论区@碳的还原性大佬指正,我思考了一下确实和元素连续没关系,应该说是利用前缀和的思想统计这样更恰当一些。。感谢大佬! 

    upd:经评论区@落雨廾匸大佬指正,O(nm)的做法不需要累加,只需要输出最后答案就行了,因为这里利用了前缀和思想f[2*m][n]+=f[2*m][n-1]+f[2*m-1][n]这一步已经把之前的累加了。感谢大佬!

    很抱歉O(nm)的做法我因为懒没写,所以有不少锅,感谢各位大佬的指正!

    放个n2m的代码:

     1 #include<stdio.h>
     2 #define it register int
     3 #define il inline
     4 const int P=1e9+7;
     5 il void mo(int &p,const int &q){p+=q,p=(p>=P?p-P:p);}
     6 int f[22][1005],m,n,ans;
     7 int main(){
     8     scanf("%d%d",&n,&m),m<<=1;
     9     for(it i=1;i<=n;++i) f[1][i]=1;
    10     for(it i=2,j,k;i<=m;++i)
    11         for(j=1;j<=n;++j)
    12             for(k=1;k<=j;++k)
    13                 mo(f[i][j],f[i-1][k]);
    14     for(it i=1;i<=n;++i) mo(ans,f[m][i]);
    15     printf("%d",ans);
    16     return 0;
    17 }
    View Code

    D:开始就想了个二分+枚举子集。萌神说只要枚举子集看是A还是B的贡献就行。所以为什么我要多个log?人间迷惑.jpg

    不过,利用状压的思想带个log似乎也可以过?而且跑的不是很慢??(据说萌神的二分T飞了??)

    做法是二分现在这个最大的最小B,然后每次把满足条件即a[i][j]>=mid的状态压缩起来,接着枚举所有压缩的状态,只要存在i|j==(1<<m)-1即全部都能选到(可以抽象理解为抽出两列至少有一列是有解的)说明可行,调整二分的上下界。思路很简单,代码很短。

     1 #include<stdio.h>
     2 #define it register int
     3 #define il inline
     4 const int M=300005;
     5 const int N=12;
     6 int p[N],id[M],a[M][N],n,m,o1,o2,lim;
     7 bool ck(const int&x){
     8     it i,j,sta;
     9     for(i=0;i<=lim;++i) id[i]=0;//清空所有状态
    10     for(i=1;i<=n;++i){
    11         sta=0;
    12         for(j=1;j<=m;++j) 
    13             if(a[i][j]>=x) sta|=p[j];//满足条件的状态压缩起来
    14         id[sta]=i;//记录这个状态是抽出第i列得到的
    15     }
    16     for(i=0;i<=lim;++i)//枚举i,j两个状态进行检查,是否可以抽出id[i]和id[j]以得到答案
    17         for(j=i;j<=lim;++j)//j从i枚举,以免重复计算
    18             if((i|j)==lim&&id[i]&&id[j]) 
    19                 return o1=id[i],o2=id[j],1;
    20     return 0;
    21 }
    22 il void ms(){//MidSearch 即二分现在最大最小的b
    23     int l=0,r=1e9,mid;
    24     while(l<=r)
    25         mid=l+r>>1,ck(mid)?l=mid+1:r=mid-1;//满足条件,说明还可能存在较大解
    26 }
    27 int main(){
    28     scanf("%d%d",&n,&m);
    29     for(it i=1,j;i<=n;++i)
    30         for(j=1;j<=m;++j) scanf("%d",&a[i][j]);
    31     lim=(1<<m)-1;
    32     for(it i=1;i<=m;++i) p[i]=1<<i-1;//常数优化
    33     ms(),printf("%d %d
    ",o1,o2);//输出任意一组满足条件的解即可 
    34     return 0;
    35 }
    View Code

    这怕不是码量最小的一次div2。。

    E:萌神说是莫队,我:??我打不开E。

    upd:早上去机房好不容易开了EF发现做过E的类似题??所以昨晚只有F有质量是吗……

    zjf神仙说E是树状数组,于是我想了5min后删了我写了一半的Splay……

    树状数组的做法是:由于最多只有m次操作,所以每次先把每个数的位置放在m+1+i处,就相当于向后推移m+1。每次把一个数提到前面就是消除他这个位置对后面的影响,并且在前面这个位置加上影响,然后更新这个数的位置。就相当于一个盒子,每次把后面的抽到前面去,统计有多少个在他前面的数。最后不要忘记统计一遍所有数的位置算出其最右位置。

     1 #include<stdio.h>
     2 #define it register int
     3 #define ct const int 
     4 #define il inline
     5 const int N=1000005;
     6 int p[N],t[N],n,m,mn[N],mx[N];
     7 il void ckMax(int &p,ct q){p=(p>q?p:q);}//常数优化
     8 il void add(it x,ct num){while(x<N) t[x]+=num,x+=(x&-x);}
     9 il void cal(it x,int &now){now=0;while(x) now+=t[x],x-=(x&-x);}//树状数组
    10 int main(){
    11     scanf("%d%d",&n,&m);
    12     it i,pos=m+1,now;
    13     for(i=1;i<=n;++i)
    14         mn[i]=mx[i]=i,p[i]=pos+i,add(p[i],1);//先把位置往后推
    15     while(m--)
    16         scanf("%d",&i),mn[i]=1,cal(p[i],now),ckMax(mx[i],now),add(p[i],-1),p[i]=pos--,add(p[i],1);//消除原来位置的影响,把这个数放到新位置上
    17     for(i=1;i<=n;++i) cal(p[i],now),ckMax(mx[i],now);
    18     for(i=1;i<=n;++i) printf("%d %d
    ",mn[i],mx[i]); 
    19     return 0;
    20 }
    View Code

    upd:意外地发现这场的ABCDE代码加起来还没Div1的E题三分之一多吧。。

    F:??打不开告辞,明早去机房补。

    upd:最近事有点多,F先咕着,寒假来补吧。

  • 相关阅读:
    Web APIs——DOM
    案例:动态生成表格
    案例:简单版发布留言功能
    案例:下拉菜单功能
    案例:tab栏切换功能(原生JS写法)
    时间复杂度
    ubuntu下安装LNMP
    btree索引和hash索引的区别
    StandardServer.await: Invalid command 'GET / HTTP/1.1' received
    MySQL----触发器
  • 原文地址:https://www.cnblogs.com/Kylin-xy/p/12194804.html
Copyright © 2011-2022 走看看