Noip模拟53 2021.9.14
T1 ZYB和售货机
首先这道题有两种做法。
一种是发现每个点都可以先被取到只剩一个,只要收益大于$0$
然后发现建一个$i->f[i]$的图时出现环,要把它去掉,
那么跑一个$tarjan$枚举每一个强联通分量然后找到收益获得最少的边删掉就行
第二种做法是建立两个数组$mx[f[i]],nx[f[i]]$分别表示对于连向$f[i]$的边中收益最大和次大的点
那么我们像跳父亲一样$dfs$,找到一个环就比较他们构成环的最大点与次大点里面差值最小的
记录这个点为$x$,那么对于$x$,需要少让他被按一次,让次大点把它按掉,这样就防止了环的影响
实现的话不用这么麻烦,直接记录差值最小值,找到环后直接减掉最小值就行。
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 namespace AE86{ 5 inline int read(){ 6 int x=0,f=1;char ch=getchar(); 7 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 8 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 9 }inline void write(int x,char opt='\n'){ 10 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 11 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 12 for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 13 }using namespace AE86; 14 15 const int NN=1e5+5,inf=0x3fffffff; 16 int n,f[NN],c[NN],d[NN],a[NN],ans,w[NN]; 17 int mx[NN],nx[NN],cer[NN],minn,num; 18 19 inline void dfs(int x){ 20 if(cer[x]==num){ans-=minn;return;} 21 if(cer[x]) return; 22 cer[x]=num; 23 if(!mx[x]) return; 24 ans+=w[mx[x]]*a[x]; 25 minn=min(minn,w[mx[x]]-w[nx[x]]); 26 if(mx[x]!=x) dfs(mx[x]); 27 } 28 29 namespace WSN{ 30 inline short main(){ 31 freopen("goods.in","r",stdin); 32 freopen("goods.out","w",stdout); 33 n=read(); 34 for(int i=1;i<=n;i++) 35 f[i]=read(),c[i]=read(),d[i]=read(),a[i]=read(); 36 for(int i=1;i<=n;i++){ 37 w[i]=d[f[i]]-c[i]; 38 if(w[mx[f[i]]]<w[i]) 39 nx[f[i]]=mx[f[i]],mx[f[i]]=i; 40 else if(w[nx[f[i]]]<w[i]) nx[f[i]]=i; 41 } 42 for(int i=1;i<=n;i++) if(!cer[i]) 43 minn=inf,++num,dfs(i); 44 write(ans); 45 return 0; 46 } 47 } 48 signed main(){return WSN::main();}
T2 ZYB玩字符串
发现每个最终的$S$里面必定有至少一个初始串$p$。
考虑枚举初始串长度,然后枚举可能的$p$,这几步正解暴力都一样
然后,如果要是打成了暴力,就会是每次暴力$check$,看看这个$p$是否能把$S$消没
你发现你只有$20$,观察测试点会有一个$ababaa$,这个卡掉了正着枚举哈希判断的你
于是你再反着做一边,得到了$50$,跟刚才的其实一样,只不过是他不把那个东西放在后边,而是中间,于是你只能打正解
关于判断,我们考虑$dp$,设$f[i][j]$表示从$[i,j]$这段区间是否合法
如果$j-i+1$是$len$的倍数,就表示$f[i][j]$区间能否被消灭
对于不是$len$倍数的区间,合法表示他有一段$p$的前缀以及若干段$p$,
那么
$1.f_{i,j}|=f_{i,j-1}\& \left [ s[j]==t[(j-i) \mod len+1] \right ] $
表示凑出前缀
$2. f_{i,j}|=f_{i,j-k*len} \& f_{j-k*len+1,j} $
表示凑出若干段$p$
然后可以记忆化,可以直接循环
有个问题需要请教:(已解决,自己弱智了)
关于代码里面的取%问题。
把取%函数去掉直接使用 $\textit{ t[((r-l+1)%len)] }$
他就一个合法的$check$也没有
使用取%函数 $\textit{ t[mo(r-l+1,len)] }$
他就对了。
inline int mo(int x,int y){ return x>y?mo(x-y,y):x; }
非常不懂,请路过的大佬帮助苣蒻
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int NN=205; 4 int T,n,tot,a[NN],t[NN],ans[NN]; 5 char s[NN]; 6 int f[NN][NN]; 7 inline int mo(int x,int y){ 8 return x>y?mo(x-y,y):x; 9 } 10 inline bool dfs(int len,int l,int r){ 11 if(l>r) return 1; 12 if(f[l][r]!=-1) return f[l][r]; 13 for(int mid=r-len;mid>=l;mid-=len) 14 if(dfs(len,l,mid)&&dfs(len,mid+1,r)) return f[l][r]=1; 15 if(a[r]==t[mo(r-l+1,len)]) return f[l][r]=dfs(len,l,r-1); 16 return f[l][r]=0; 17 } 18 19 namespace WSN{ 20 inline short main(){ 21 freopen("string.in","r",stdin); 22 freopen("string.out","w",stdout); 23 cin>>T; 24 while(T--){ 25 scanf("%s",s+1); n=strlen(s+1); 26 tot=0; memset(ans,0,sizeof(ans)); 27 for(int i=1;i<=n;i++) a[i]=(int)s[i]-'a'+1; 28 for(int len=1;len<=n;len++){ 29 if(n%len) continue; 30 for(int l=1;l<=n-len+1;l++){ 31 memset(f,-1,sizeof(f)); 32 for(int i=l;i<=l+len-1;i++) t[i-l+1]=a[i]; 33 // printf("len=%d l=%d r=%d check=%d\n",len,l,l+len-1,dfs(len,1,n)); 34 if(dfs(len,1,n)){ 35 if(!tot){ 36 tot=len; 37 for(int i=1;i<=tot;i++) ans[i]=t[i]; 38 }else{ 39 for(int i=1;i<=tot;i++) if(ans[i]>t[i]){ 40 for(int j=1;j<=tot;j++) ans[j]=t[j]; 41 break; 42 } 43 } 44 } 45 } 46 if(tot) break; 47 } 48 for(int i=1;i<=tot;i++) printf("%c",ans[i]+'a'-1); puts(""); 49 } 50 return 0; 51 } 52 } 53 signed main(){return WSN::main();}
T3 午餐
最短路神题
题解不知道在说什么,反正我是看完$DeepinC$博客才看懂的题解,于是。。。
(感谢学长花大力气打出如此详细的题解)
理解过之后就可以搞出来了
1 #include<bits/stdc++.h> 2 #define fi first 3 #define se second 4 #define mp make_pair 5 #define pii pair<int,int> 6 using namespace std; 7 namespace AE86{ 8 inline int read(){ 9 int x=0,f=1;char ch=getchar(); 10 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 11 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 12 }inline void write(int x,char opt='\n'){ 13 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 14 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 15 for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 16 }using namespace AE86; 17 18 const int NN=2e5+5,inf=0x3fffffff; 19 int n,m,a[NN],f[NN],st[NN],ed[NN],h[NN],L[NN],R[NN]; 20 struct SNOW{int to,l,r,next;}e[NN<<1]; int head[NN],rp; 21 inline void add(int x,int y,int l,int r){ 22 e[++rp]=(SNOW){y,l,r,head[x]}; head[x]=rp; 23 e[++rp]=(SNOW){x,l,r,head[y]}; head[y]=rp; 24 } 25 struct node{ 26 int id,data; 27 friend bool operator<(node a,node b){ 28 return a.data<b.data; 29 } 30 }; priority_queue<node> Q; 31 priority_queue<pii > q; 32 bool vis[NN]; 33 34 inline void dij(){ 35 memset(f,0x3f,sizeof(f)); 36 Q.push((node){1,0}); f[1]=0; 37 while(!Q.empty()){ 38 int x=Q.top().id,y=Q.top().data; Q.pop(); 39 for(int i=head[x];i;i=e[i].next){ 40 int y=e[i].to,val=max(h[y]+1,max(f[x],e[i].l)); 41 if(f[y]>val&&val<=e[i].r) 42 Q.push((node){y,f[y]=val}); 43 } 44 } 45 } 46 47 namespace WSN{ 48 inline short main(){ 49 freopen("lunch.in","r",stdin); 50 freopen("lunch.out","w",stdout); 51 n=read(); m=read(); 52 for(int i=1;i<=m;i++){ 53 int u=read(),v=read(),l=read(),r=read(); 54 add(u,v,l,r); st[i]=u; ed[i]=v; L[i]=l; R[i]=r; 55 } 56 for(int i=1;i<=n;i++){ 57 a[i]=read(); if(a[i]!=-1) h[i]=-1; 58 else q.push(mp(h[i]=inf,i)); 59 } 60 while(!q.empty()){ 61 int x=q.top().se,y=q.top().fi; q.pop(); 62 for(int i=head[x];i;i=e[i].next) 63 if(h[x]>=e[i].r && h[e[i].to]<e[i].l) 64 q.push(mp(h[e[i].to]=e[i].l,e[i].to)); 65 } 66 if(h[1]>0) return puts("Impossible"),0; dij(); 67 for(int i=1;i<=n;i++) if(a[i]==1&&f[i]>1e9){ 68 return puts("Impossible"),0; 69 } 70 for(int i=1;i<=m;i++){ 71 int u=st[i],v=ed[i],l=L[i],r=R[i]; 72 if(f[u]>r||f[v]>r){write(l);continue;} 73 write(max(l,max(f[u],f[v]))); 74 } 75 return 0; 76 } 77 } 78 signed main(){return WSN::main();}
T4 计数
考场上打了一个$m=0$的卡特兰数就跑了。。。。
但是发现考后$20$分的居然都是$TLE$,(反正我不会打暴力)
所以我随口说一下,关于二叉树的种类问题是$Cat_n=C_{2n}^{n}-C_{2n}^{n-1}$
但是这和正解毫不相关
正解考虑二叉树的前序遍历性质:
- 这棵树的编号满足小根堆性质
- 这棵树的某个子树编号连续,为$[root,root+size-1]$
- 这棵树左子树节点编号都小于右子树
- 这棵树任意一个有左子节点的节点的左子节点编号必定为该节点编号$+1$
说白了就是$dfs$序。但是$rvalue$学长总结的好就抄过来了
关于中序遍历的限制,每个点对会被强制规定一个限制,考虑以前序遍历表示的点对$(a,b)$
如果限制a在b后,a一定是b的祖先
限制a在b前,两种位置关系都有可能
发现这两种限制实际上就是在限制以一个点为根的字数大小
如果$a>b$那么会卡$b$的下限,反之卡$a$的上限
所以可以预处理出每个点的子树大小上限和下限然后记忆化
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(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 12 }using namespace AE86; 13 14 const int mod=1e9+7; 15 int T,n,m,lml[405],lmr[405]; 16 int C[805][805],dp[405][405]; 17 inline int mo(int x){return x>=mod?x-mod:x;} 18 inline void pre(){ 19 C[0][0]=1; 20 for(int i=1;i<=800;i++){ 21 C[i][0]=C[i][i]=1; 22 for(int j=1;j<i;j++){ 23 C[i][j]=mo(C[i-1][j]+C[i-1][j-1]); 24 } 25 } 26 } 27 inline int Catlan(int n){ 28 return mo(C[n*2][n]-C[n*2][n-1]+mod); 29 } 30 31 inline int dfs(int x,int siz){ 32 if(dp[x][siz]!=-1) return dp[x][siz]; 33 if(!siz) return dp[x][siz]=1; 34 dp[x][siz]=0; 35 for(int i=lml[x];i<=lmr[x]&&i<siz;i++) 36 dp[x][siz]=mo(dp[x][siz]+1ll*dfs(x+1,i)*dfs(x+1+i,siz-1-i)%mod); 37 return dp[x][siz]; 38 } 39 40 namespace WSN{ 41 inline short main(){ 42 freopen("count.in","r",stdin); 43 freopen("count.out","w",stdout); 44 T=read(); pre(); 45 while(T--){ 46 n=read(); m=read(); 47 if(!m) {write(Catlan(n));continue;} 48 memset(dp,-1,sizeof(dp)); 49 for(int i=1;i<=n;i++) lml[i]=0,lmr[i]=n-i+1; 50 for(int i=1;i<=m;i++){ 51 int a=read(),b=read(); 52 if(a>b) lml[b]=max(lml[b],a-b); 53 else lmr[a]=min(lmr[a],b-a-1); 54 } 55 write(dfs(1,n)); 56 } 57 return 0; 58 } 59 } 60 signed main(){return WSN::main();}