11月11日考试 题解(exgcd+树形DP+模拟+最短路+哈希+主席树)
T1 方程的解
题目大意:求$ax+by=c$的正整数解的个数。
exgcd板子。求出来$x$取得最小正整数解时$y$的解;再求出$y$的最小正整数解。两者之差除以$\frac{a}{\gcd (a,b)}$加一即为答案。注意细节。
代码:
#include<cstdio> #include<iostream> #define int long long using namespace std; int T,a,b,c,x,y,d; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void exgcd(int a,int b,int c,int &x,int &y,int &d) { if(!b) y=0,d=a,x=c/a; else exgcd(b,a%b,c,y,x,d),y-=(a/b)*x; } signed main() { T=read(); while(T--) { a=read(),b=read(),c=read(); if (!a&&!b) { if (!c) puts("ZenMeZheMeDuo"); else puts("0"); continue; } if (c<0) a=-a,b=-b,c=-c; bool fa=0,fb=0; if (a<0) a=-a,fa=1; if (b<0) b=-b,fb=1; exgcd(a,b,c,x,y,d); if (a*x+b*y!=c){puts("0");continue;} if (fa) a=-a,x=-x; if (fb) b=-b,y=-y; if (a==0) { if (y<=0) puts("0"); else puts("ZenMeZheMeDuo"); continue; } if (b==0) { if (x<=0) puts("0"); else puts("ZenMeZheMeDuo"); continue; } if (a*b<0){puts("ZenMeZheMeDuo");continue;} if(a<0) a=-a,b=-b,c=-c; a/=d;b/=d;c/=d;x%=b; if(x<=0) x+=b; y=(c-a*x)/b; int miny=y%a; if(miny<=0) miny+=a; int res=(miny>y)?0:(y-miny)/a+1; if(res>65535) puts("ZenMeZheMeDuo"); else printf("%lld\n",res); } return 0; }
T2 染色
原题目:P3177。
设$f_{i,j}$表示$i$为根的子树内有$j$个黑点对答案的贡献。然后大力转移就好。可以参考题解。
代码:
#include<bits/stdc++.h> #define ll long long #define gc getchar #define maxn 2005 using namespace std; inline ll read(){ ll a=0;int f=0;char p=gc(); while(!isdigit(p)){f|=p=='-';p=gc();} while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=gc();} return f?-a:a; } struct ahaha{ int w,to,next; }e[maxn<<1];int tot,head[maxn]; inline void add(int u,int v,int w){ e[tot].w=w,e[tot].to=v,e[tot].next=head[u];head[u]=tot++; } int n,m,sz[maxn]; ll f[maxn][maxn]; void dfs(int u,int fa){ sz[u]=1;f[u][0]=f[u][1]=0; for(int i=head[u];~i;i=e[i].next){ int v=e[i].to;if(v==fa)continue; dfs(v,u);sz[u]+=sz[v]; for(int j=min(m,sz[u]);j>=0;--j){ if(f[u][j]!=-1) f[u][j]+=f[v][0]+(ll)sz[v]*(n-m-sz[v])*e[i].w; for(int k=min(j,sz[v]);k;--k){ if(f[u][j-k]==-1)continue; ll val=(ll)(k*(m-k)+(sz[v]-k)*(n-m-sz[v]+k))*e[i].w; f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]+val); } } } } int main(){memset(head,-1,sizeof head); n=read();m=read(); if(n-m<m)m=n-m; for(int i=1;i<n;++i){ int u=read(),v=read(),w=read(); add(u,v,w);add(v,u,w); }memset(f,-1,sizeof f); dfs(1,-1); printf("%lld",f[1][m]); return 0; }
T3 光
原题目:CF274E
大力模拟。先考虑暴力怎么做:每次按照光线的方向dfs,直到碰到一个障碍点再反射。这样寻找复杂度是$O(n)$的。而我们现在要将其优化成$\log n$。具体做法是把所有对角线编号,对于障碍点扔进STL::set里面,查询的时候lower_bound查找。然后就是一些细节了。考试时候好不容易想出来了做法结果写挂了,还不如交暴力QAQ
代码:
#include<set> #include<map> #include<cstdio> #include<iostream> using namespace std; const int N=200005; int n,m,k,sx,sy,d; char ch[5]; long long ans; set<int> s1[N],s2[N]; map<pair<int,int>,bool> mp; struct node{ int x,y,d; }; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } inline int get(int x,int y,int d){ return d==1?x-y+m+1:x+y; } inline bool same(node a,node b){ return a.x==b.x&&a.y==b.y&&a.d==b.d; } inline bool check(int x,int y){ return mp[make_pair(x,y)]; } inline void add(int x,int y) { s1[get(x,y,1)].insert(x); s2[get(x,y,2)].insert(x); mp[make_pair(x,y)]=1; } //NE(-1,1) NW(-1,-1) SE(1,1) SW(1,-1) //1:NW 2:NE 3:SE 4:SW //d:from which dinodetion inline pair<node,int> dfs(node u) { node res; set<int>::iterator it; if(u.d==1) { it=s1[get(u.x,u.y,1)].lower_bound(u.x);it--; res.x=u.x-(abs(*it-u.x)-1); res.y=u.y-(abs(*it-u.x)-1); if(check(res.x-1,res.y)&&check(res.x,res.y-1))res.d=3; else if(check(res.x-1,res.y)){res.y--;res.d=4;} else if(check(res.x,res.y-1)){res.x--;res.d=2;} else res.d=3; } if(u.d==2) { it=s2[get(u.x,u.y,2)].lower_bound(u.x);it--; res.x=u.x-(abs(*it-u.x)-1); res.y=u.y+(abs(*it-u.x)-1); if(check(res.x-1,res.y)&&check(res.x,res.y+1))res.d=4; else if(check(res.x-1,res.y)){res.y++;res.d=3;} else if(check(res.x,res.y+1)){res.x--;res.d=1;} else res.d=4; } if(u.d==3) { it=s1[get(u.x,u.y,1)].lower_bound(u.x); res.x=u.x+(abs(*it-u.x)-1); res.y=u.y+(abs(*it-u.x)-1); if(check(res.x+1,res.y)&&check(res.x,res.y+1))res.d=1; else if(check(res.x+1,res.y)){res.y++;res.d=2;} else if(check(res.x,res.y+1)){res.x++;res.d=4;} else res.d=1; } if(u.d==4) { it=s2[get(u.x,u.y,2)].lower_bound(u.x); res.x=u.x+(abs(*it-u.x)-1); res.y=u.y-(abs(*it-u.x)-1); if(check(res.x+1,res.y)&&check(res.x,res.y-1))res.d=2; else if(check(res.x+1,res.y)){res.y--;res.d=1;} else if(check(res.x,res.y-1)){res.x++;res.d=3;} else res.d=2; } return make_pair(res,abs(*it-u.x)); } bool judge(node u) { node res=u; do { pair<node,int> cur=dfs(u); ans+=(long long)cur.second; switch(cur.first.d) { case 1:if(u.d==3)return 0;break; case 2:if(u.d==4)return 0;break; case 3:if(u.d==1)return 0;break; case 4:if(u.d==2)return 0;break; } u=cur.first; }while(!same(res,u)); return 1; } int main() { n=read();m=read();k=read(); for (int i=0;i<=m+1;i++) add(0,i),add(n+1,i); for (int i=0;i<=n+1;i++) add(i,0),add(i,m+1); for(int i=1;i<=k;i++) { int x=read(),y=read(); add(x,y); } int x=read(),y=read(),d; char ch[5];scanf("%s",ch+1); if(ch[1]=='N'&&ch[2]=='W')d=1; if(ch[1]=='N'&&ch[2]=='E')d=2; if(ch[1]=='S'&&ch[2]=='E')d=3; if(ch[1]=='S'&&ch[2]=='W')d=4; node st={x,y,d}; st=dfs(st).first; if(!judge(st)) { ans--; switch(st.d) { case 1:st.d=3;break; case 2:st.d=4;break; case 3:st.d=1;break; case 4:st.d=2;break; } judge(st); } printf("%lld",ans); return 0; }
T4 无向图
题目大意:给定无向图G =(V,E),其中V={1,2,.....n },以及函数p:V →V和f:V →V,
保证 {p(i)|i∈V }= V。对于 G 的任意路径 P 和 v∈V ,记
C(P,v)=u∈V(P)[f(u)=v] ,其中V(P)为路径 P 经过的点集(包括起点和终点)。
对于两条路径P1和P2, P1和P2的比较方法如下:取最小的i,满足C(P1,p(i))
C(P2,p(i)),则定义C(Pj ,p(i)) 较大的那条路径 Pj较大。若所有C(P1,p(i))=
C(P2,p(i)),则认为 P1 和 P2 一样大。对于给定的无向图G =(V ,E)及函数 p:V → V 和 f : V → V ,求以1为起
点、 n为终点的所有路径中最小的一条。
题面很诡异。大概意思就是对于路径上的所有点,把它们$f$值扔进桶里面,然后按照$p$依次比较字典序。找到字典序最小的那个并输出桶内的值。
暴力的做法是直接跑最短路,暴力比较。这样修改的复杂度是$O(1)$的,比较的复杂度$O(n)$。我们可以尝试平衡复杂度。一个比较套路的方法是利用主席树。更新属于继承过来,比较实际上可以把两条路径的字典序哈希起来比较。这样修改和查询的复杂度都是$\log n$的。
代码:
#include<queue> #include<cstdio> #include<iostream> #define ull unsigned long long using namespace std; const int base=131; const int N=100005,M=500005; int n,m,t,p[N],f[N],rev[N]; int rt[N],ls[N*21],rs[N*21],cnt[N*21],tot; ull b[N],val[N*21]; int head[N],edge_cnt; bool vis[N],tag[N]; struct e{ int next,to; }edge[M*2]; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void add(int from,int to) { edge[++edge_cnt]=(e){head[from],to}; head[from]=edge_cnt; } inline void update(int &cur,int l,int r,int pos) { int x=++tot; ls[x]=ls[cur],rs[x]=rs[cur]; cnt[x]=cnt[cur]+1,val[x]=val[cur]+b[pos],cur=x; if(l==r) return; int mid=(l+r)>>1; if(pos<=mid) update(ls[cur],l,mid,pos); else update(rs[cur],mid+1,r,pos); } inline int query(int x,int y,int l,int r) { ull v=val[y]; int c=cnt[y]; if(t&&t>=l&&t<=r) v+=b[t],c++; if(val[x]==v) return 0; if(!cnt[x]) return -1; if(!c) return 1; if(l==r) { if(cnt[x]==c) return 0; if(cnt[x]>c) return 1; if(cnt[x]<c) return -1; } int mid=(l+r)>>1; int q=query(ls[x],ls[y],l,mid); if(q) return q; return query(rs[x],rs[y],mid+1,r); } struct node{ int id; }; bool operator < (const node &a,const node &b){ return query(rt[a.id],rt[b.id],1,n)>=0; } inline void dijkstra() { priority_queue<node> q; update(rt[1],1,n,rev[f[1]]);q.push({1});tag[1]=1; while(!q.empty()) { int x=q.top().id; q.pop(); if(vis[x]) continue; vis[x]=true; for(int i=head[x];i;i=edge[i].next) { int y=edge[i].to; if(!tag[y]) tag[y]=true,rt[y]=rt[x],update(rt[y],1,n,rev[f[y]]),q.push({y}); else { t=rev[f[y]]; if(query(rt[y],rt[x],1,n)==1) rt[y]=rt[x],update(rt[y],1,n,rev[f[y]]),q.push({y}); t=0; } } } } inline void dfs(int cur,int l,int r) { if(!cur) return; if(l==r) { for(int i=1;i<=cnt[cur];++i) printf("%d ",p[l]); return; } int mid=(l+r)>>1; dfs(ls[cur],l,mid),dfs(rs[cur],mid+1,r); } int main() { n=read(),m=read(),b[0]=1; for(int i=1;i<=n;++i) b[i]=b[i-1]*base; for(int i=1;i<=n;++i) p[i]=read(),rev[p[i]]=i; for(int i=1;i<=n;++i) f[i]=read(); for(int i=1;i<=m;++i) { int x=read(),y=read(); add(x,y),add(y,x); } dijkstra(),dfs(rt[n],1,n); return 0; }