題解 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\n",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\n",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\n",mn[i],mx[i]); 
19     return 0;
20 }
View Code

upd:意外地發現這場的ABCDE代碼加起來還沒Div1的E題三分之一多吧。。

F:??打不開告辭,明早去機房補。

upd:最近事有點多,F先咕着,寒假來補吧。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM