2021.9.14考试总结[NOIP模拟53]
T1 ZYB和售货机
容易发现把每个物品都买成$1$是没有影响的。
然后考虑最后一个物品的方案,如果从$f_i$向$i$连边,发现每个点有一个出度多个入度,可以先默认每个物品都能买且最大获利,这样可以建出每个点出度入度都是$1$的图。
把所有边都连上是一个基环树,所以建出的若干个联通图中只有一个环。而我们要做的工作就是用最小代价把这个环断掉,形成的树上所有边都可以对答案贡献。
记每个物品的最大获利和次大获利,在图上$DFS$,每到一个点先加上最大获利,记录路径上最大获利与次大获利差的最小值,如果当前这一块是环,把答案减去这个最小值即可。
$code:$
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 5 namespace IO{ 6 inline int read(){ 7 char ch=getchar(); int x=0,f=1; 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(); } 10 return x*f; 11 } 12 inline void write(int x,char sp){ 13 char ch[20]; int len=0; 14 if(x<0){ putchar('-'); x=~x+1; } 15 do{ ch[len++]=x%10+(1<<4)+(1<<5); x/=10; }while(x); 16 for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp); 17 } 18 inline int max(int x,int y){ return x<y?y:x; } 19 inline int min(int x,int y){ return x<y?x:y; } 20 inline void swap(int &x,int &y){ x^=y^=x^=y; } 21 inline void chmax(int &x,int y){ x=x<y?y:x; } 22 inline void chmin(int &x,int y){ x=x<y?x:y; } 23 } using namespace IO; 24 25 const int NN=1e6+5; 26 int n,st,ans,cnt,mn,bel[NN],f[NN],c[NN],d[NN],a[NN],mx[NN],sc[NN],val[NN]; 27 bool vis[NN]; 28 29 void dfs(int x){ 30 if(bel[x]==cnt) return ans-=mn,void(); 31 if(bel[x]) return; 32 if(!mx[x]) return; 33 bel[x]=cnt; 34 ans+=val[mx[x]]*a[x]; 35 chmin(mn,val[mx[x]]-val[sc[x]]); 36 if(mx[x]!=x)dfs(mx[x]); 37 } 38 signed main(){ 39 freopen("goods.in","r",stdin); 40 freopen("goods.out","w",stdout); 41 n=read(); 42 for(int i=1;i<=n;i++) 43 f[i]=read(), c[i]=read(), d[i]=read(), a[i]=read(); 44 for(int i=1;i<=n;i++){ 45 val[i]=d[f[i]]-c[i]; 46 if(val[i]>val[mx[f[i]]]) sc[f[i]]=mx[f[i]], mx[f[i]]=i; 47 else if(val[i]>val[sc[f[i]]]) sc[f[i]]=i; 48 } 49 for(int i=1;i<=n;i++) 50 if(!bel[i]) ++cnt,mn=INT_MAX,dfs(i); 51 write(ans,'\n'); 52 return 0; 53 }
T2 ZYB玩字符串
可以想到暴力枚举子串暴力删除$check$,但假了,因为删除顺序可能导致结果不同。
考虑$DP$来$check$。设$f_{l,r}$表示母串$s$内$l$到$r$区间内的串能否被选出的子串$p$匹配。如果$r-l+1$不是$|p|$的倍数,那么要求剩余部分拼成$p$的前缀。
有转移:
$f_{l,r}|=f_{l,r-1}\text{&}[s_r=q_{(l-r)\textit{ mod }|p|+1}]$
$f_{l,r}|=f_{l,r-k\times |p|}\text{&}f_{t-k\times |p|+1,r}$
可以用字符集出现次数的$gcd$剪枝,跑得很快(其实还能记忆化,会更快
$code:$
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 namespace IO{ 5 inline int read(){ 6 char ch=getchar(); int x=0,f=1; 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(); } 9 return x*f; 10 } 11 inline void write(int x,char sp){ 12 char ch[20]; int len=0; 13 if(x<0){ putchar('-'); x=~x+1; } 14 do{ ch[len++]=x%10+(1<<4)+(1<<5); x/=10; }while(x); 15 for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp); 16 } 17 inline int max(int x,int y){ return x<y?y:x; } 18 inline int min(int x,int y){ return x<y?x:y; } 19 inline void swap(int &x,int &y){ x^=y^=x^=y; } 20 inline void chmax(int &x,int y){ x=x<y?y:x; } 21 inline void chmin(int &x,int y){ x=x<y?x:y; } 22 } using namespace IO; 23 24 const int NN=210; 25 int T,n,g,hh,res,sah[30],has[30],apr[30],tms[30],f[NN][NN]; 26 char s[NN],tmp[NN],ans[NN]; 27 inline int gcd(int a,int b){ return !b?a:gcd(b,a%b); } 28 29 void init(){ 30 hh=0; res=INT_MAX; ans[1]='z'+1; 31 memset(apr,0,sizeof(apr)); memset(has,0,sizeof(has)); 32 for(int i=1;i<=n;i++){ 33 if(!has[s[i]-'a']) has[s[i]-'a']=++hh, sah[hh]=s[i]-'a'; 34 ++apr[has[s[i]-'a']]; 35 } 36 g=gcd(apr[1],apr[2]); 37 for(int i=3;i<=hh;i++) g=gcd(g,apr[i]); 38 for(int i=1;i<=hh;i++) tms[i]=apr[i]/g; 39 } 40 bool begin(int l,int r){ 41 if(n%(r-l+1)) return 0; 42 memset(apr,0,sizeof(apr)); 43 for(int i=l;i<=r;i++) ++apr[has[s[i]-'a']]; 44 for(int i=1;i<=hh;i++){ 45 if(!apr[i]) return 0; 46 if(i>1&&apr[i]/tms[i]!=apr[i-1]/tms[i-1]) return 0; 47 } 48 return 1; 49 } 50 void upd(int l,int r){ 51 int len=r-l+1; 52 for(int i=l;i<=r;i++) tmp[i-l+1]=s[i]; 53 if(len<res){ 54 res=len; 55 for(int i=1;i<=len;i++) ans[i]=tmp[i]; 56 return; 57 } 58 if(len>res) return; 59 for(int i=1;i<=len;i++){ 60 if(tmp[i]>ans[i]) return; 61 if(tmp[i]<ans[i]){ 62 for(int j=1;j<=len;j++) ans[i]=tmp[i]; 63 return; 64 } 65 } 66 } 67 bool check(int l,int r){ 68 memset(f,0,sizeof(f)); 69 int len=r-l+1; f[l][r]=1; 70 for(int i=l;i<=r;i++) tmp[i-l+1]=s[i]; 71 for(int i=1;i<=n-len+1;i++) 72 if(s[i]==tmp[1]) f[i][i]=1; 73 for(int i=2;i<=n;i++) 74 for(int j=1;j<=n-i+1;j++){ 75 int x=j,y=j+i-1,ne=(i-1)%len+1; 76 f[x][y]|=f[x][y-1]&(s[y]==tmp[ne]); 77 for(int k=len;k<i;k+=len) 78 f[x][y]|=f[x][y-k]&f[y-k+1][y]; 79 } 80 return f[1][n]; 81 } 82 83 signed main(){ 84 freopen("string.in","r",stdin); 85 freopen("string.out","w",stdout); 86 T=read(); 87 while(T--){ 88 scanf("%s",s+1); n=strlen(s+1); 89 init(); 90 for(int i=1;i<=n;i++){ 91 if(res!=INT_MAX) break; 92 for(int j=i;j<=n;j++){ 93 if(!begin(j-i+1,j)) continue; 94 if(check(j-i+1,j)) upd(j-i+1,j); 95 } 96 } 97 for(int i=1;i<=res;i++) putchar(ans[i]);putchar('\n'); 98 } 99 return 0; 100 }
T3 午餐
神仙题,参考$DC$学长博客,讲得超清楚
$code:$
1 #include<bits/stdc++.h> 2 #define x first 3 #define y second 4 #define mp make_pair 5 using namespace std; 6 typedef pair<int,int> pii; 7 8 namespace IO{ 9 inline int read(){ 10 char ch=getchar(); int x=0,f=1; 11 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 12 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } 13 return x*f; 14 } 15 inline void write(int x,char sp){ 16 char ch[20]; int len=0; 17 if(x<0){ putchar('-'); x=~x+1; } 18 do{ ch[len++]=x%10+(1<<4)+(1<<5); x/=10; }while(x); 19 for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp); 20 } 21 inline int max(int x,int y){ return x<y?y:x; } 22 inline int min(int x,int y){ return x<y?x:y; } 23 inline void swap(int &x,int &y){ x^=y^=x^=y; } 24 inline void chmax(int &x,int y){ x=x<y?y:x; } 25 inline void chmin(int &x,int y){ x=x<y?x:y; } 26 } using namespace IO; 27 28 const int NN=2e5+5,inf=0x3fffffff; 29 int n,m,u[NN],v[NN],L[NN],R[NN],is[NN],h[NN],f[NN]; 30 int idx,to[NN<<1],nex[NN<<1],head[NN],l[NN<<1],r[NN<<1]; 31 inline void add(int a,int b,int c,int d){ 32 to[++idx]=b; nex[idx]=head[a]; head[a]=idx; l[idx]=c; r[idx]=d; 33 to[++idx]=a; nex[idx]=head[b]; head[b]=idx; l[idx]=c; r[idx]=d; 34 } 35 36 void dij(){ 37 priority_queue<pii,vector<pii>,greater<pii> >q; 38 for(int i=1;i<=n;i++) 39 if(is[i]==-1){ 40 h[i]=inf; 41 q.push(mp(inf,i)); 42 } 43 while(!q.empty()){ 44 int x=q.top().y,y=q.top().x; 45 q.pop(); 46 for(int i=head[x];i;i=nex[i]){ 47 int vv=to[i]; 48 if(h[x]>=r[i]&&h[vv]<l[i]){ 49 h[vv]=l[i]; 50 q.push(mp(h[vv],vv)); 51 } 52 } 53 } 54 priority_queue<pii >qq; qq.push(mp(0,1)); 55 for(int i=2;i<=n;i++) f[i]=inf; 56 while(!qq.empty()){ 57 int x=qq.top().y,y=qq.top().x; 58 qq.pop(); 59 for(int i=head[x];i;i=nex[i]){ 60 int vv=to[i],val=max(h[vv]+1,max(f[x],l[i])); 61 if(f[vv]>val&&val<=r[i]){ 62 f[vv]=val; 63 qq.push(mp(f[vv],vv)); 64 } 65 } 66 } 67 } 68 69 signed main(){ 70 freopen("lunch.in","r",stdin); 71 freopen("lunch.out","w",stdout); 72 n=read(); m=read(); 73 for(int i=1;i<=m;i++){ 74 u[i]=read(),v[i]=read(),L[i]=read(),R[i]=read(); 75 add(u[i],v[i],L[i],R[i]); 76 } 77 for(int i=1;i<=n;i++) is[i]=read(); 78 dij(); 79 if(h[1]){ puts("Impossible"); return 0; } 80 for(int i=1;i<=n;i++) if(is[i]==1&&f[i]==inf){ puts("Impossible"); return 0; } 81 for(int i=1;i<=m;i++) 82 if(f[u[i]]<=R[i]&&f[v[i]]<=R[i]) chmax(L[i],max(f[u[i]],f[v[i]])); 83 for(int i=1;i<=m;i++) write(L[i],'\n'); 84 return 0; 85 }
T4 计数
前序遍历中编号满足$a<b$,只有两种情况:
$1$.$a$是$b$的祖先
$2$.$a$在$LCA$的左子树,$b$在$LCA$的右子树。
考虑加限制,强制令前序遍历中$a<b$,那么中序遍历中:
$1$.$a$在$b$前,那么$b$只能在$a$的右子树或是上面第二种情况,可以总结为$b$不在$a$
$2$.$a$在$b$后,那么$b$只能在$a$的左子树中。
两种限制对$a$的左子树大小都有要求,$1$要求大小小于$b-a$,$2$要求大小不小于$b-a$。
于是可以求出$a$左子树大小范围,考虑$DP$。
$f_{i,j}$表示$i$子树大小为$j$的方案,有:
$f_{i,j}=\sum_kf_{i+1,k}\times f_{i+k+1,i-k-1}$,$k$为$i$可行的左子树大小。
$code:$
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 5 namespace IO{ 6 inline int read(){ 7 char ch=getchar(); int x=0,f=1; 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(); } 10 return x*f; 11 } 12 inline void write(int x,char sp){ 13 char ch[20]; int len=0; 14 if(x<0){ putchar('-'); x=~x+1; } 15 do{ ch[len++]=x%10+(1<<4)+(1<<5); x/=10; }while(x); 16 for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp); 17 } 18 inline int max(int x,int y){ return x<y?y:x; } 19 inline int min(int x,int y){ return x<y?x:y; } 20 inline void swap(int &x,int &y){ x^=y^=x^=y; } 21 inline void chmax(int &x,int y){ x=x<y?y:x; } 22 inline void chmin(int &x,int y){ x=x<y?x:y; } 23 } using namespace IO; 24 25 const int NN=405,p=1e9+7; 26 int t,n,m,u,v,l[NN],r[NN],f[NN][NN]; 27 28 int dfs(int pos,int num){ 29 if(f[pos][num]!=-1) return f[pos][num]; 30 if(pos>n||!num) return f[pos][num]=1; 31 if(num>n-pos+1||num<l[pos]) return f[pos][num]=0; 32 int up=min(num-1,r[pos]); f[pos][num]=0; 33 for(int i=l[pos];i<=up;i++) 34 (f[pos][num]+=dfs(pos+1,i)*dfs(pos+i+1,num-i-1)%p)%=p; 35 return f[pos][num]; 36 } 37 38 signed main(){ 39 // freopen("count.in","r",stdin); 40 // freopen("count.out","w",stdout); 41 t=read(); 42 while(t--){ 43 n=read(); m=read(); 44 memset(f,-1,sizeof(f)); 45 for(int i=1;i<=n;i++) 46 l[i]=0, r[i]=n-i; 47 for(int i=1;i<=m;i++){ 48 u=read(), v=read(); 49 if(u<v) chmin(r[u],v-u-1); 50 else chmax(l[v],u-v); 51 } 52 write(dfs(1,n),'\n'); 53 } 54 return 0; 55 }