====== 2020牛客暑期多校训练营(第十场) ====== [[https://ac.nowcoder.com/acm/contest/5675|比赛网址]] ===== 训练结果 ===== * 时间:''2020-8-10 12:00~17:00'' * rank:''146/?'' * 完成情况:''3/4/10'' ===== 题解 ===== ===== A.Permutation ===== === 题意 === 给了一个质数 $p$ ,让你构造一个 $1到p-1$ 的数列,满足 $x_{i+1}≡2x_i(modp) \ or \ x_{i+1}≡3x_i(modp)$ === 题解 === 直接从开始一直乘二,不能乘就乘三,就过了 #include #include #include #include #define ll long long using namespace std; int read() { int k=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) k=k*10+c-'0';return k*f; } int T,d[2000005],ans[2000055],vis[10005]; int ksm(int x,int k,int mod) { int ans=1; for(;k;k>>=1,x=1ll*x*x%mod) if(k&1) ans=1ll*ans*x%mod; return ans; } int main() { for(T=read();T;T--) { int p=read(); if(p==2) {puts("1");continue;} int now=1,tot=0,sum,cnt=1,len; for(;;) { if(d[now]) break; tot++; d[now]=1; ans[tot]=now; now=now*2%p; }// cout< ===== C.Decrement on the Tree ===== === 题意 === 有一棵边权树,每次你可以选择一条路径(路径上边权都大于0)把所有的边权减一,问最小的操作次数将所有边权都改为 0。每次询问会修改一条边。 === 题解 === 发现不带修就是一个树型dp,$f_i$ 表示改 $i$ 的子树要的最小步数减去父亲到他的边权,$f_i= max(mx_i-v_i,(sum_i-v_i)/2)$ ,$mx_i$ 指到子树的最大边权,$sum_i$ 指到子树的所有边权和,$v_i$ 指父亲到他的边权,这样发现 $f_i$ 只和 $i$ 连出去的边有关,所以每次修改一条边,只有两个点的 $f_i$ 发生变化,暴力改就行了 #include #include #include #include #include typedef long long ll; using namespace std; int read() { int k=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) k=k*10+c-'0';return k*f; } const int N=200055; int n,m,tot,fa[N],to[N],head[N],nextt[N],dep[N]; int a[N],b[N],c[N]; ll res,sum[N],mx[N],mx2[N],val[N],f[N],w[N]; multiset st[N]; void add(int a,int b,int c) { to[++tot]=b; nextt[tot]=head[a]; head[a]=tot; w[tot]=c; } void dfs(int u,int F,int d) { dep[u]=d;fa[u]=F; for(int i=head[u];i;i=nextt[i]) if(to[i]!=F) { val[to[i]]=w[i]; dfs(to[i],u,d+1); sum[u]+=w[i]; if(w[i]>mx[u]) mx[u]=w[i]; st[u].insert(w[i]); } if(sum[u]<=val[u]) f[u]=0; else res+=(f[u]=max(mx[u]-val[u],(sum[u]+1-val[u])/2)); } int query(int x) { if(!st[x].size()) return 0; multiset::iterator it; it=st[x].end();it--; return *it; } int main() { n=read();m=read(); for(int i=1;iy) f[u]=max(query(u)-y,(sum[u]+1-y)/2); res+=f[u]; res-=f[v];f[v]=0; ll s=sum[v]-c[x]+y,mxx=query(v); if(s>val[v]) f[v]=max(mxx-val[v],(s+1-val[v])/2); res+=f[v]; val[u]=y;sum[v]+=y-c[x];c[x]=y; printf("%lld\n",res); } return 0; } ===== D.Hearthstone Battlegrounds ===== === 题意 === === 题解 === ===== E.Game ===== === 题意 === === 题解 === ===== G.Math Test ===== === 题意 === 二分签到题 ===== J.Identical Trees ===== === 题意 === 有两棵有根树,标号不同,但保证同构,问最少修改一棵树上多少点编号,使得这两棵树完全相同 $n\leq500$ === 题解 === 设$dp[x][y]$ 表示第一棵树以$x$为根,第二棵树以$y$为根,最少需要改多少个。这个状态合法的前提是$x和y$同构,然后把他们的儿子之前如果同构的话就可以匹配,匹配的代价是$dp[v1][v2]$($v1$$,v2$是x,y的儿子),此处需要一个最小代价最大匹配,根据最后的代价进行转移。$dp[x][y]=mincost+(x!=y)$ 我感觉这玩意复杂度是$O(n^5)$的。。。但是跑的飞快。 注意!!!判同构的时候既要判哈希值也要判size!!!本题有两个点卡哈希! #include using namespace std; #define mem(a,b) memset(a,b,sizeof(a)) typedef long long LL; typedef pair PII; #define X first #define Y second typedef unsigned long long ULL; inline int read() { int x=0,f=1;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while(isdigit(c)){x=x*10+c-'0';c=getchar();} return x*f; } const int maxn=600; int n,rt1,rt2,a,size1[maxn],size2[maxn],dp[maxn][maxn]; vector G1[maxn],G2[maxn]; int prime[maxn],len; ULL H1[maxn],H2[maxn]; struct ZKW { int n,m,s,t,cost,ans; int d[maxn<<1],vis[maxn<<1],first[maxn<<1],inq[maxn<<1],next[510*510*2]; struct Edge {int from,to,flow,cost;}edges[510*510*2]; void init(int n) { this->n=n;m=0; memset(first,-1,sizeof(first)); memset(inq,0,sizeof(inq)); } void AddEdge(int from,int to,int cap,int cost) { edges[m]=(Edge){from,to,cap,cost};next[m]=first[from];first[from]=m++; edges[m]=(Edge){to,from,0,-cost};next[m]=first[to];first[to]=m++; } int BFS() { for(int i=1;i<=n;i++) d[i]=maxn; queue Q;Q.push(t);d[t]=0; while(!Q.empty()) { int x=Q.front();Q.pop();inq[x]=0; for(int i=first[x];i!=-1;i=next[i]) { Edge& e=edges[i^1]; if(e.flow&&d[e.from]>d[x]+e.cost) { d[e.from]=d[x]+e.cost; if(!inq[e.from]) inq[e.from]=1,Q.push(e.from); } } } for(int i=0;i<=m;i++) edges[i].cost+=d[edges[i].to]-d[edges[i].from]; cost+=d[s];return d[s]!=maxn; } int DFS(int x,int a) { if(x==t||!a) {ans+=cost*a;return a;} int flow=0,f;vis[x]=1; for(int i=first[x];i!=-1;i=next[i]) { Edge& e=edges[i]; if(e.flow&&!e.cost&&!vis[e.to]&&(f=DFS(e.to,min(e.flow,a)))) { e.flow-=f;edges[i^1].flow+=f; a-=f;flow+=f;if(!a) break; } } return flow; } int solve(int s,int t) { this->s=s;this->t=t;ans=cost=0; while(BFS()) do memset(vis,0,sizeof(vis));while(DFS(s,maxn)); return ans; } }sol; bool vis[10010]; void init(int x) { for(int i=2;i<=x;i++) { if(!vis[i])prime[++len]=i; for(int j=1;j<=len && i*prime[j]<=x;j++) { vis[i*prime[j]]=1; if(i%prime[j]==0)break; } } } void dfs1(int now) { H1[now]=size1[now]=1; for(int v:G1[now]) { dfs1(v); H1[now]+=H1[v]*(ULL)prime[size1[v]]; size1[now]+=size1[v]; } } void dfs2(int now) { H2[now]=size2[now]=1; for(int v:G2[now]) { dfs2(v); H2[now]+=H2[v]*(ULL)prime[size2[v]]; size2[now]+=size2[v]; } } void DP(int x,int y) { dp[x][y]=maxn; if(H1[x]!=H2[y] || size1[x]!=size2[y])return; if(size1[x]==1 && size2[y]==1) { dp[x][y]=(x!=y); return; } for(int v1:G1[x]) for(int v2:G2[y])DP(v1,v2); int N=2+G1[x].size()+G2[y].size(),s=1,t=N; sol.init(N); for(int i=0;i ===== ===== ===== 训练实况 ===== ===== 训练总结 ===== wxg: 菜是原罪 hxm:菜啊 fyh:对网络流复杂度不熟,导致不敢写J