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 #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=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();}
$UPD 2021.10.8$打过了正解,比较神仙的贪心
发现选择一个节点,他的所有祖先都必须要选,所以保证中序遍历最优也就等价于保证前序遍历最优
然后考虑先使用一个$dp$计算出深度为$x$的$avl$树至少有几个节点 $dp[x]=dp[x-1]+dp[x-2]+1$
证明考虑把两个子树拼起来加上一个根节点就行。
预处理完之后考虑如何谈心
记录几个数组
$mdp[x]$表示以$x$为根的子树里面最深的节点的深度
$h[x]$表示构造出来的合法的$avl$树里面的$x$的深度
$mh[x]$表示以$x$为根的子树深度至少为多少才合法,就是一个下界。
那么在按照$dfs$序遍历树的时候模拟选择一个节点的过程然后看选完这个节点后是否合法$\leq k$,
合法的话就真正的给答案加上这个点以及其祖先
1 #include<bits/stdc++.h> 2 using namespace std; 3 namespace AE86{ 4 inline int read(){ 5 int x=0,f=1;char ch=getchar(); 6 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 8 }inline void write(int x,char opt='\n'){ 9 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 10 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 11 for(int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 12 }using namespace AE86; 13 const int NN=5e5+5; 14 int n,k,root,fa[NN],son[NN][2],cnt[41],dep[NN],mdp[NN],h[NN],mh[NN]; 15 bool ans[NN]; 16 inline void getdep(int f,int x){ 17 dep[x]=dep[f]+1; mdp[x]=dep[x]; 18 if(son[x][0]) 19 getdep(x,son[x][0]),mdp[x]=max(mdp[x],mdp[son[x][0]]); 20 if(son[x][1]) 21 getdep(x,son[x][1]),mdp[x]=max(mdp[x],mdp[son[x][1]]); 22 } 23 inline int calc(int x,int num=0){ 24 int y=max(h[x],dep[x]); 25 while(x){ 26 if(!ans[x]) ++num; 27 y=max(y,h[x]); 28 if(x<fa[x]&&!ans[son[fa[x]][1]]) 29 num+=cnt[max(y-1,mh[son[fa[x]][1]])-dep[fa[x]]]; 30 x=fa[x]; 31 } 32 return num; 33 } 34 inline void add(int x){ 35 h[x]=max(h[x],dep[x]); 36 int y=h[x]; 37 while(x){ 38 if(!ans[x]) ans[x]=1,--k; 39 h[x]=max(h[x],y); 40 if(x<fa[x]&&!ans[son[fa[x]][1]]&&son[fa[x]][1]) 41 mh[son[fa[x]][1]]=max(mh[son[fa[x]][1]],h[x]-1); 42 x=fa[x]; 43 } 44 } 45 inline void slove(int x){ 46 // int num=calc(x); 47 // printf("x=%d num=%d k=%d ans=",x,num,k); 48 // for(int i=1;i<=n;i++) cout<<ans[i];cout<<endl; 49 if(calc(x)<=k) add(x); 50 if(son[x][1]&&son[x][0]){ 51 if(mdp[son[x][0]]<mh[x]){ 52 mh[son[x][0]]=max(mh[x]-1,mh[son[x][0]]); 53 mh[son[x][1]]=max(mh[x],mh[son[x][1]]); 54 }else{ 55 mh[son[x][0]]=max(mh[x],mh[son[x][0]]); 56 mh[son[x][1]]=max(mh[x]-1,mh[son[x][1]]); 57 } 58 slove(son[x][0]); slove(son[x][1]); 59 } 60 else{ 61 if(son[x][0]){ 62 mh[son[x][0]]=max(mh[son[x][0]],mh[x]); 63 slove(son[x][0]); 64 } 65 if(son[x][1]){ 66 mh[son[x][1]]=max(mh[son[x][1]],mh[x]); 67 slove(son[x][1]); 68 } 69 } 70 } 71 namespace WSN{ 72 inline short main(){ 73 // freopen("in.in","r",stdin); freopen("bao.out","w",stdout); 74 freopen("avl.in","r",stdin); freopen("avl.out","w",stdout); 75 n=read(); k=read(); 76 cnt[1]=1; cnt[2]=2; for(int i=3;i<=40;i++) cnt[i]=cnt[i-1]+cnt[i-2]+1; 77 for(int i=1,x;i<=n;i++) x=read(),(x==-1)?(fa[i]=0,root=i):(fa[i]=x),son[fa[i]][fa[i]<i]=i; 78 getdep(0,root); slove(root); for(int i=1;i<=n;i++) printf("%d",ans[i]); puts(""); 79 return 0; 80 } 81 } 82 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();}