2023年暑假集训总结/6.27
6-27
T1图
一姬在一个 n 个点和 m 条边无向图中迷路了,她不知道她现在在哪里。每个点上有一个宝玉,一姬要收集 k 个宝玉才能缔结契约,走出这个无向图。图中被访问的点不能再访问第二次,经过每条边需要一定的时间,求所需的最大时间是多少?注: 走到的点宝玉必须要取走。收集到 k 个宝玉必须离开无向图
考试时出了一点细节错误wa了一个点,正解应将k=2/3/4/5/6分别讨论,k=2时分别枚举边即可,k=3/4时需枚举两条边并处理,k=5/6时需要再次预处理每个点延伸出去的边的前7集合。
std:
#include<bits/stdc++.h> const int N=1e4+5,B=500; using namespace std; mt19937 rnd(19260817); int n,m,k,col[N],S; struct edge{int x,y,z;}e[N]; int f[N][64]; int check() { for(int i=1;i<=n;i++) { for(int j=0;j<S;j++)f[i][j]=-2e9; f[i][1<<col[i]]=0; } for(int j=0;j<S;j++) for(int i=1,xx,yy,zz;i<=m;i++) { xx=e[i].x;yy=e[i].y;zz=e[i].z; if((j>>col[xx])&1)f[xx][j]=max(f[xx][j],f[yy][j^(1<<col[xx])]+zz); if((j>>col[yy])&1)f[yy][j]=max(f[yy][j],f[xx][j^(1<<col[yy])]+zz); } int res=-1; for(int i=1;i<=n;i++)res=max(res,f[i][S-1]); return res; } int ans; void solve() { cin>>n>>m>>k;S=1<<k; for(int i=1;i<=m;i++)cin>>e[i].x>>e[i].y>>e[i].z; ans=-1; for(int i=1;i<=B;i++) { for(int j=1;j<=n;j++)col[j]=rnd()%k; ans=max(ans,check()); } cout<<ans<<'\n'; } int T; int main() { freopen("graph.in","r",stdin); freopen("graph.out","w",stdout); ios::sync_with_stdio(false);cin.tie(0); cin>>T; while(T--)solve(); }
T2随机
给定一张 n 个点,m 条边的图,你可以进行如下两种操作:
1. 选择一条边并删掉,这个操作代价为 a。
2. 选择一个点并删掉与之关联的所有边,这个操作代价为 b。你希望以最小的代价删掉图中所有的边。请你输出这个最小代价
考试时模拟退火拿了21分,最后发现模拟退火的方式不对,换一个calc思路就A了
std:
#include<bits/stdc++.h> using namespace std; int n,m,a,b,v[45]; mt19937 rnd(time(0)); basic_string<int>e[45]; int x[45],y[45],dfn[45]; void dfs(int x,int fa,int col) { dfn[x]=1;v[x]=col; for(auto y:e[x]) if(!dfn[y]) dfs(y,x,col^1); } int minn,sum1,sum2; void lian(int id) { int tval; for(int i=1;i<=n;i++)dfn[i]=0; dfs(1,0,id); sum1=sum2=0; for(int i=1;i<=m;i++)if(!v[x[i]]&&!v[y[i]])sum1++; for(int i=1;i<=n;i++)sum2+=v[i]; minn=min(minn,sum1*a+sum2*b); for(int ci=1,p;ci<=5000;ci++) { p=rnd()%n+1; v[p]^=1; sum1=0;sum2=0; for(int i=1;i<=m;i++)if(!v[x[i]]&&!v[y[i]])sum1++; for(int i=1;i<=n;i++)sum2+=v[i]; tval=sum1*a+sum2*b; if(sum1*a+sum2*b<minn) { minn=tval; continue; } if(sum1*a+sum2*b==minn&&rnd()&1)continue; v[p]^=1; } } vector<int>gx,gy; void SA() { for(int cnt=1,minm,bian;cnt<n;cnt++) { minm=m; for(int TT=1;TT<=15;TT++) { for(int i=1;i<=cnt;i++)v[i]=1; for(int i=cnt+1;i<=n;i++)v[i]=0; shuffle(v+1,v+n+1,rnd); gx.clear();gy.clear(); for(int i=1;i<=n;i++) if(v[i])gx.emplace_back(i); else gy.emplace_back(i); bian=m; for(int j=1,temp,xx,yy;j<=1200;j++) { xx=rnd()%((int)gx.size());yy=rnd()%((int)gy.size()); swap(gx[xx],gx.back()); swap(gy[yy],gy.back()); v[gx.back()]=0;v[gy.back()]=1; swap(gx.back(),gy.back()); temp=0; for(int i=1;i<=m;i++) if(!v[x[i]]&&!v[y[i]])temp++; if(temp<bian){bian=temp;continue;} if(temp==bian&&rnd()&1)continue; swap(gx.back(),gy.back()); v[gx.back()]=1;v[gy.back()]=0; } minm=min(minm,bian); } minn=min(minn,minm*a+cnt*b); } } void solve() { cin>>n>>m>>a>>b;minn=min(a*m,b*n); for(int i=1;i<=n;i++)e[i].clear(); for(int i=1;i<=m;i++) { cin>>x[i]>>y[i]; e[x[i]]+=y[i];e[y[i]]+=x[i]; } for(int i=1;i<=5;i++){lian(0);lian(1);} SA(); cout<<minn<<'\n'; } int T; signed main() { freopen("random.in","r",stdin); freopen("random.out","w",stdout); srand(time(0)); ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); cin>>T; while(T--)solve(); }
T3子串
八木唯给你了一个 n,输出一个 01 序列,使得其中的本质不同子串最多。
我们定义两个子串不同,当且仅当有这两个子串长度不一样或者长度一样且有任意一位不一样。
子串的定义:原字符串中连续的一段字符组成的字符串。
M.A.Martin 于 1934 年证明了以下贪心算法对所有的 n ≥ 2 都可以构造出一个长度2 n 的 De Bruijn 序列:
1. 写出 n 个 0。
2. 如果在序列尾部添加一个 1 后,和前面相连构成已经出现过的长为 n 的 01 子串,
则在序列尾部添加一个 0,否则在序列尾部添加一个 1。
3. 序列若还不够 2 n 项,则返回步骤 2。否则序列就是一个长度为 2 n 次的 De Bruijn
如果这是序列还没有满,再往后面添0即可。
std:
#include <bits/stdc++.h> using namespace std; int lim,n; string s,temp; unordered_map<string,int>mp; signed main() { freopen("substring.in","r",stdin); freopen("substring.out","w",stdout); cin>>n; if(n==1){cout<<0<<'\n';return 0;} for(lim=0;(1<<lim)<=n;lim++);lim--; if((1<<lim)==n||(1<<lim)+lim-1==n) { for(int i=1;i<=lim;i++)s+='0'; mp[s]=1; for(int now=lim+1;now<=n;now++) { temp=s+'1'; if(!mp[temp.substr(now-lim,lim)]) { mp[temp.substr(now-lim,lim)]=1; s=s+'1'; continue; } s=s+'0'; mp[s.substr(now-lim,lim)]=1; } while(s.size()!=n)s+='0'; cout<<s<<'\n'; return 0; } }