Noip模擬70 2021.10.6
T1 暴雨
放在第一道的神仙題,不同的做法,吊人有的都在用線段樹維護$set$預處理
我是直接$dp$的,可能程式碼的複雜度比那種的稍微小一點
設$f[i][j][p][0/1]$表示考慮了前$i$列,裡面的最大值高度是$j$,
並且後面還至少存在高度為$j$的土塊,在前$i$列挖平了$p$個土塊,積水的體積是奇數或者偶數的方案數
採用刷表更新$dp$值的方法,更新$f[i][j][k][u]$的所有可能到達的狀態
可能有人問陣列怎麼開,因為$k \leq 26$所以最大值的哪一維只記錄前$k+1$大即可
適當的給原陣列離散化。
記錄幾個值
$val[i][j]$表示前$i$列第$j$大的土塊的高度,
$cnt[i]$表示前$i$列有幾個可以轉移的前$j$大值(記錄可轉移的狀態個數)
然後考慮$dp$,用$i$更新$i+1$
考慮兩種情況,
1.$val[i][j] \geq a[i+1]$
$f[i+1][J][k][0/1]+=f[i][j][k][0/1]$
$f[i+1][J][k+1][0/1]+=f[i][j][k][0/1]$
2.$val[i][j] \leq a[i+1]$
$f[i+1][a_{i+1}][k][0/1]+=f[i][j][k][0/1]$
$f[i+1][J][k+1][0/1]+=f[i][j][k][0/1]$
直接做轉移即可,然後因為$dp$轉移的時候有一個限制,就是保證後面至少有一個高度為$j$的土塊
所以為了讓轉移合法,我們從前往後,從後往前分別轉移一次
然後考慮對$dp$值合併合併的時候保證$g,f$陣列滿足轉移時候的關係即可
寫程式碼的時候注意不要往超越邊界的地方轉移,卡一下邊界即可
1 #include<bits/stdc++.h> 2 #define sit multiset<int>::iterator 3 using namespace std; 4 namespace AE86{ 5 #define out(x) cout<<#x<<":"<<x<<endl 6 #defineView Codefuck cout<<"fuck"<<endl 7 inline int read(){ 8 int x=0,f=1;char ch=getchar(); 9 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 10 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 11 }inline void write(int x,char opt='\n'){ 12 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 13 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 14 for(int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 15 }using namespace AE86; 16 const int NN=25010,mod=1e9+7; 17 int n,K,h[NN],id[NN],ans; 18 struct DP{ 19 int a[NN],f[NN][26][26][2],val[NN][26],cnt[NN]; 20 multiset<int> S;unordered_map<int,int> mp[NN]; 21 inline void calc(){ 22 S.insert(0); 23 for(int i=1;i<=n;i++){ 24 S.insert(a[i]); sit it=S.end(); 25 for(int j=1;j<=K+1;j++){ 26 if(it==S.begin()) break; --it; 27 val[i][mp[i][*it]=++cnt[i]]=*it; 28 } 29 } 30 f[0][1][0][0]=1; val[0][cnt[0]=1]=0; 31 for(int i=0;i<n;i++) for(int j=1;j<=cnt[i];j++) for(int k=0;k<=K;k++) 32 for(int u=0;u<=1;u++) if(f[i][j][k][u]){ 33 if(val[i][j]>=a[i+1]){ 34 int J=mp[i+1][val[i][j]],sta=u^(val[i][j]-a[i+1]&1); 35 f[i+1][J][k][sta]=(f[i+1][J][k][sta]+f[i][j][k][u])%mod; 36 sta=u^(val[i][j]&1); 37 if(k+1<=K) f[i+1][J][k+1][sta]=(f[i+1][J][k+1][sta]+f[i][j][k][u])%mod; 38 }else{ 39 int J=mp[i+1][a[i+1]],sta=u^(val[i][j]&1); 40 f[i+1][J][k][u]=(f[i+1][J][k][u]+f[i][j][k][u])%mod; 41 J=mp[i+1][val[i][j]]; 42 if(k+1<=K) f[i+1][J][k+1][sta]=(f[i+1][J][k+1][sta]+f[i][j][k][u])%mod; 43 } 44 } 45 } 46 }F,G; 47 namespace WSN{ 48 inline short main(){ 49 freopen("rain.in","r",stdin); 50 freopen("rain.out","w",stdout); 51 n=read(); K=read(); for(int i=1;i<=n;i++) h[i]=read(),F.a[i]=G.a[n-i+1]=h[i],id[i]=i; 52 F.calc(); G.calc(); 53 sort(id+1,id+n+1,[](int x,int y)->bool{return h[x]>h[y];}); 54 for(int i=1;i<=n;i++){ 55 if(h[id[i]]<h[id[K+1]]) break; 56 int now=id[i],val=h[now]; 57 for(int j=F.cnt[now-1];j;j--){ 58 if(F.val[now-1][j]>val) break; 59 for(int k=G.cnt[n-now];k;k--){ 60 if(G.val[n-now][k]>=val) break; 61 for(int u=0;u<=K;u++){ 62 ans=(ans+1ll*F.f[now-1][j][u][0]*G.f[n-now][k][K-u][0]%mod)%mod; 63 ans=(ans+1ll*F.f[now-1][j][u][1]*G.f[n-now][k][K-u][1]%mod)%mod; 64 } 65 } 66 } 67 } write(ans%mod); 68 return 0; 69 } 70 } 71 signed main(){return WSN::main();}
T2 AVL樹
不知道有沒有什麼簡便的暴力
反正我打了$98$行才拿到了$20$分
當時就知道這一場要崩掉了,花了將近一個小時調暴力(?)
早知道去打別的題了,比如$T4$的六十分感覺貌似不難想,就是可能是調有點費勁
然後不會,就咕咕咕了
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 namespace AE86{ 5 #define out(x) cout<<"x="<<x<<endl 6 #define fuck cout<<"fuck"<<endl 7 inline int read(){ 8 int x=0,f=1;char ch=getchar(); 9 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 10 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 11 }inline void write(int x,char opt='\n'){ 12 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 13 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 14 for(int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 15 }using namespace AE86; 16 const int NN=5e5+5; 17 int n,k,fa[NN],son[NN][2],root,dep[NN],mdp[NN]; 18 bool vis[NN]; 19 inline void getdep(int f,int x){ 20 if(son[x][0]){ 21 dep[son[x][0]]=dep[x]+1; 22 getdep(x,son[x][0]); 23 } 24 if(son[x][1]){ 25 dep[son[x][1]]=dep[x]+1; 26 getdep(x,son[x][1]); 27 } 28 } 29 inline void dfs(int f,int x){ 30 if(!vis[x]) return; 31 mdp[x]=dep[x]; 32 if(son[x][0]){ 33 dfs(x,son[x][0]); 34 mdp[x]=max(mdp[x],mdp[son[x][0]]); 35 } 36 if(son[x][1]){ 37 dfs(x,son[x][1]); 38 mdp[x]=max(mdp[x],mdp[son[x][1]]); 39 } 40 } 41 vector<int> pre; 42 inline void bian(int x){ 43 if(son[x][0]&&vis[son[x][0]]) bian(son[x][0]); 44 pre.push_back(x); 45 if(son[x][1]&&vis[son[x][1]]) bian(son[x][1]); 46 } 47 int ans[NN],sta; 48 namespace WSN{ 49 inline short main(){ 50 freopen("avl.in","r",stdin); 51 freopen("avl.out","w",stdout); 52 n=read(); k=read(); memset(ans,0x3f,sizeof(ans)); 53 for(int i=1,x;i<=n;i++){ 54 x=read(); fa[i]=x; son[x][i>x]=i; 55 if(x==-1) root=i; 56 } 57 if(k==1){ 58 for(int i=1;i<=n;i++) printf(fa[i]!=-1?"0":"1"); 59 return puts(""),0; 60 } 61 getdep(-1,root); 62 for(int i=1;i<(1<<n);++i) if((i&(1<<root-1))&&__builtin_popcount(i)==k){ 63 for(int i=1;i<=n;++i) mdp[i]=0; 64 for(int j=1;j<=n;++j) if(i&(1<<j-1)) vis[j]=1; 65 dfs(-1,root); bool flag=0; 66 for(int j=1;j<=n;++j){ 67 if(vis[j]&&(vis[son[j][0]]||vis[son[j][1]])){ 68 int a=mdp[son[j][0]]-dep[son[j][0]]; 69 int b=mdp[son[j][1]]-dep[son[j][1]]; 70 if(!vis[son[j][0]]) a=-1; 71 if(!vis[son[j][1]]) b=-1; 72 if(abs(a-b)>1){flag=1;break;} 73 } 74 } 75 if(!flag){ 76 pre.clear(); bian(root); 77 if(pre.size()==k){ 78 for(int j=0;j<pre.size();++j){ 79 if(ans[j+1]>pre[j]){ 80 for(int j=0;j<pre.size();++j) ans[j+1]=pre[j]; 81 sta=i; break; 82 } 83 if(ans[j+1]<pre[j]) break; 84 } 85 } 86 } 87 for(int j=1;j<=n;++j) if(i&(1<<j-1)) vis[j]=0; 88 } 89 for(int i=1;i<(1<<n);++i) if(sta==i){ 90 for(int j=1;j<=n;++j) 91 if(i&(1<<j-1)) printf("1"); 92 else printf("0"); puts(""); 93 break; 94 } 95 return 0; 96 } 97 } 98 signed main(){return WSN::main();}暴力辛酸淚
T3 挖掘機
檔名叫$blueshit$就非常妙
自己的$OJ$評測姬比較快,有的拿分塊寫的
但是正解好像是倍增
看得到行數比較小,而且每一行不聯絡,所以直接一行一行搞
然後發現找到一個在$[l,r]$區間內的$X$就可以消掉連續的$k$個,這樣一定最優
那麼我們以$k$為一段,倍增去跳這個段數,然後你選擇從$r$忘前跳和從$l$往後跳都可以,就是往後跳的時候邊界的處理比較麻煩,建議往前跳
1 #include<bits/stdc++.h> 2 using namespace std; 3 namespace AE86{ 4 #define out(x) cout<<"x="<<x<<endl 5 #define fuck cout<<"fuck"<<endl 6 inline int read(){ 7 int x=0,f=1;char ch=getchar(); 8 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 9 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 10 }inline void write(int x,char opt='\n'){ 11 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 12 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 13 for(int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 14 }using namespace AE86; 15 const int NN=1e5+5; 16 int h,w,k,q,d,l,r,a[13][NN],sum[13][NN],b[13][NN],ans; 17 int bz[13][NN][18]; 18 char ch[NN]; 19 namespace WSN{ 20 inline short main(){ 21 freopen("blueshit.in","r",stdin); 22 freopen("blueshit.out","w",stdout); 23 h=read();w=read();k=read();q=read(); 24 for(int i=1;i<=h;++i){ scanf("%s",ch+1); bz[i][w+1][0]=w+1; 25 for(int j=w;j;--j) bz[i][j][0]=ch[j]=='X'?j:bz[i][j+1][0]; 26 } 27 for(int i=1;i<=h;++i) for(int j=w+1;j;--j) for(int u=1;u<=17;++u) 28 bz[i][j][u]=bz[i][min(bz[i][j][u-1]+k,w+1)][u-1]; 29 while(q--){ 30 d=read();l=read();r=read();ans=0; 31 for(int i=1;i<=d;++i){ int pos=l; 32 for(int j=17;~j;--j) if(bz[i][pos][j]<=r) 33 ans+=1<<j,pos=min(bz[i][pos][j]+k,w+1); 34 } 35 write(ans); 36 } 37 return 0; 38 } 39 } 40 signed main(){return WSN::main();}向後跳
1 #include<bits/stdc++.h> 2 using namespace std; 3 namespace AE86{ 4 #define out(x) cout<<"x="<<x<<endl 5 #define fuck cout<<"fuck"<<endl 6 inline int read(){ 7 int x=0,f=1;char ch=getchar(); 8 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 9 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 10 }inline void write(int x,char opt='\n'){ 11 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 12 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 13 for(int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 14 }using namespace AE86; 15 const int NN=1e5+5; 16 int h,w,k,q,d,l,r,a[13][NN],sum[13][NN],b[13][NN],ans; 17 int bz[13][NN][18]; 18 char ch[NN]; 19 namespace WSN{ 20 inline short main(){ 21 freopen("blueshit.in","r",stdin); 22 freopen("blueshit.out","w",stdout); 23 h=read();w=read();k=read();q=read(); 24 for(int i=1;i<=h;++i){ scanf("%s",ch+1); 25 for(int j=1;j<=w;++j) bz[i][j][0]=ch[j]=='X'?j:bz[i][j-1][0]; 26 } 27 for(int i=1;i<=h;i++) for(int j=1;j<=w;j++) for(int u=1;u<=17;u++) 28 bz[i][j][u]=bz[i][max(bz[i][j][u-1]-k,0)][u-1]; 29 while(q--){ 30 d=read();l=read();r=read();ans=0; 31 for(int i=1;i<=d;i++){ int pos=r; 32 for(int j=17;~j;j--) if(bz[i][pos][j]>=l) 33 ans+=1<<j,pos=max(bz[i][pos][j]-k,0); 34 } write(ans); 35 } 36 return 0; 37 } 38 } 39 signed main(){return WSN::main();}向前跳
T4 遊戲
$n^2$的暴力考後打了出來
列舉兩個字串的初始匹配點,然後二分找到最長的匹配長度,更新平局的樹狀陣列,然後從最長匹配點後面比較字典序
然後更新兩者誰勝誰負的樹狀陣列,最後直接查詢即可
1 #include<stdio.h> 2 #include<cstring> 3 #define int long long 4 typedef unsigned long long ULL; 5 const ULL base=131; 6 const int NN=8010; 7 int n,m,k; 8 char A[NN],B[NN]; 9 ULL a[NN],b[NN],pw[NN]; 10 struct BIT{ 11 int tr[NN]; 12 inline void update(int x,int v){ 13 ++x;while(x<NN) tr[x]+=v,x+=(x&(-x)); 14 } 15 inline int query(int x,int ans=0){ 16 ++x;while(x) ans+=tr[x],x-=(x&(-x)); 17 return ans; 18 } 19 }c1,c2,c3; 20 21 inline int _min_(int a,int b){return a<b?a:b;} 22 inline ULL get(int l,int r,ULL *a){ 23 return a[r]-a[l-1]*pw[r-l+1]; 24 } 25 inline int gcd(int a,int b){ 26 return b?gcd(b,a%b):a; 27 } 28 inline bool check(int p1,int p2,int len){ 29 if(p1+len-1>n||p2+len-1>m) return 0; 30 return get(p1,p1+len-1,a)==get(p2,p2+len-1,b); 31 } 32 int st1,st2,ed,l,r,ans,mid; 33 namespace WSN{ 34 inline short main(){ 35 freopen("game.in","r",stdin); 36 freopen("game.out","w",stdout); 37 pw[0]=1; for(int i=1;i<NN;++i) pw[i]=pw[i-1]*base; 38 scanf("%s",A+1); scanf("%s",B+1); 39 n=strlen(A+1); m=strlen(B+1); k=_min_(n,m); 40 for(int i=1;i<=n;++i) a[i]=a[i-1]*base+(ULL)(A[i]-'a'+1); 41 for(int i=1;i<=m;++i) b[i]=b[i-1]*base+(ULL)(B[i]-'a'+1); 42 for(int i=1;i<=n;++i){ 43 for(int j=1;j<=m;++j){ 44 ans=0; ed=_min_(n-i+1,m-j+1); 45 if(A[i]==B[j]){ 46 l=0,r=ed; 47 while(l<=r){ 48 mid=l+r>>1; 49 if(check(i,j,mid)) l=mid+1,ans=mid; 50 else r=mid-1; 51 }c2.update(1,1); c2.update(ans+1,-1); 52 }st1=i+ans,st2=j+ans; 53 if(A[st1]<B[st2]) c1.update(ans+1,1),c1.update(ed+1,-1); 54 else c3.update(ans+1,1),c3.update(ed+1,-1); 55 } 56 } 57 for(int a1,a2,a3,GCD,tot,b1,b2,b3,C1,C2,C3,i=1;i<=k;++i){ 58 a1=c1.query(i),a2=c2.query(i),a3=c3.query(i); 59 GCD=gcd(a1,gcd(a2,a3)),tot=a1/GCD+a2/GCD+a3/GCD; 60 b1=a1/GCD,b2=a2/GCD,b3=a3/GCD; 61 C1=gcd(b1,tot),C2=gcd(b2,tot),C3=gcd(b3,tot); 62 printf("%lld/%lld %lld/%lld %lld/%lld\n",b1/C1,tot/C1,b2/C2,tot/C2,b3/C3,tot/C3); 63 } 64 return 0; 65 } 66 } 67 signed main(){return WSN::main();}RE 60