2019.6.1 模拟赛——[ 费用流 ][ 数位DP ][ 计算几何 ]
第一题:http://codeforces.com/contest/1061/problem/E
把点集分成不相交的,然后跑费用流即可。然而错了一个点。
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } int Mn(int a,int b){return a<b?a:b;} const int N=1505,M=N*8,INF=N; int n,tot,hd[N],xnt=1,to[M],nxt[M],cap[M],w[M]; int dis[N],info[N],pre[N]; bool ins[N]; int S,T,flow,val,ans; bool fg; queue<int> q; void add(int x,int y,int z,int k) { to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt; cap[xnt]=z; w[xnt]=k; to[++xnt]=x;nxt[xnt]=hd[y];hd[y]=xnt; cap[xnt]=0; w[xnt]=-k; } namespace I{ const int N=505,M=N<<1; int c[N],hd[N],xnt,to[M],nxt[M],fa[N]; int h2[N],xt2,t2[M],nt2[M]; int lm[N],q[N],he,tl,tmp; void ad1(int x,int y) {to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;} void ad2(int x,int y) {t2[++xt2]=y;nt2[xt2]=h2[x];h2[x]=xt2;} void ini_dfs(int cr,int f) { fa[cr]=f; for(int i=hd[cr],v;i;i=nxt[i]) if((v=to[i])!=f) ini_dfs(v,cr); } void dfs(int cr) { add(tot,cr,1,c[cr]); for(int i=hd[cr],v;i;i=nxt[i]) if((v=to[i])!=fa[cr]) { if(lm[v]){q[++tl]=v; tmp-=lm[v];} else dfs(v); } } void ini_dfs2(int cr,int f) { fa[cr]=f; for(int i=h2[cr],v;i;i=nt2[i]) if((v=t2[i])!=f) ini_dfs2(v,cr); } void dfs2(int cr) { add(cr,tot,1,0); for(int i=h2[cr],v;i;i=nt2[i]) if((v=t2[i])!=fa[cr]) { if(lm[v]){q[++tl]=v; tmp-=lm[v];} else dfs2(v); } } void solve() { int s0=rdn(),s1=rdn(); for(int i=1;i<=n;i++)c[i]=rdn(); for(int i=1,u,v;i<n;i++) u=rdn(),v=rdn(),ad1(u,v),ad1(v,u); for(int i=1,u,v;i<n;i++) u=rdn(),v=rdn(),ad2(u,v),ad2(v,u); int Q=rdn(); for(int i=1;i<=Q;i++) {int d=rdn(); lm[d]=rdn();} val=lm[s0]; he=tl=0; q[++tl]=s0; ini_dfs(s0,0); while(he<tl) { int k=q[++he]; tmp=lm[k]; tot++; dfs(k); add(S,tot,tmp,0); } memset(lm,0,sizeof lm); Q=rdn(); for(int i=1;i<=Q;i++) {int d=rdn(); lm[d]=rdn();} he=tl=0; q[++tl]=s1; fa[s1]=0; ini_dfs2(s1,0); while(he<tl) { int k=q[++he]; tmp=lm[k]; tot++; dfs2(k); add(tot,T,tmp,0); } if(val!=lm[s1])fg=1; } } bool spfa() { memset(dis,-4,sizeof dis); dis[S]=0; info[S]=INF; info[T]=0; q.push(S); ins[S]=1; while(q.size()) { int k=q.front(); q.pop(); ins[k]=0; for(int i=hd[k],v;i;i=nxt[i]) if(cap[i]&&dis[v=to[i]]<dis[k]+w[i]) { dis[v]=dis[k]+w[i]; pre[v]=i; info[v]=Mn(info[k],cap[i]); if(!ins[v])ins[v]=1,q.push(v); } } return info[T]; } void ek() { int k=info[T]; flow+=k; for(int i=pre[T];i;i=pre[to[i^1]]) { cap[i]-=k; cap[i^1]+=k; ans+=k*w[i];} } int main() { freopen("w.in","r",stdin); freopen("w.out","w",stdout); tot=n=rdn(); S=0; T=++tot; I::solve(); if(fg){puts("-1");return 0;} while(spfa())ek(); if(flow==val)printf("%d\n",ans); else puts("-1"); return 0; }
后来发现是题目里写了 “每个人的条件不会自相矛盾” ,但那个点却自相矛盾了。
判一下即可。
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } int Mn(int a,int b){return a<b?a:b;} const int N=1505,M=N*8,INF=N; int n,tot,hd[N],xnt=1,to[M],nxt[M],cap[M],w[M]; int dis[N],info[N],pre[N]; bool ins[N]; int S,T,flow,val,ans; bool fg; queue<int> q; void add(int x,int y,int z,int k) { to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt; cap[xnt]=z; w[xnt]=k; to[++xnt]=x;nxt[xnt]=hd[y];hd[y]=xnt; cap[xnt]=0; w[xnt]=-k; } namespace I{ const int N=505,M=N<<1; int c[N],hd[N],xnt,to[M],nxt[M],fa[N]; int h2[N],xt2,t2[M],nt2[M]; int lm[N],q[N],he,tl,tmp; void ad1(int x,int y) {to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;} void ad2(int x,int y) {t2[++xt2]=y;nt2[xt2]=h2[x];h2[x]=xt2;} void ini_dfs(int cr,int f) { fa[cr]=f; for(int i=hd[cr],v;i;i=nxt[i]) if((v=to[i])!=f) ini_dfs(v,cr); } void dfs(int cr) { add(tot,cr,1,c[cr]); for(int i=hd[cr],v;i;i=nxt[i]) if((v=to[i])!=fa[cr]) { if(lm[v]){q[++tl]=v; tmp-=lm[v];} else dfs(v); } } void ini_dfs2(int cr,int f) { fa[cr]=f; for(int i=h2[cr],v;i;i=nt2[i]) if((v=t2[i])!=f) ini_dfs2(v,cr); } void dfs2(int cr) { add(cr,tot,1,0); for(int i=h2[cr],v;i;i=nt2[i]) if((v=t2[i])!=fa[cr]) { if(lm[v]){q[++tl]=v; tmp-=lm[v];} else dfs2(v); } } void solve() { int s0=rdn(),s1=rdn(); for(int i=1;i<=n;i++)c[i]=rdn(); for(int i=1,u,v;i<n;i++) u=rdn(),v=rdn(),ad1(u,v),ad1(v,u); for(int i=1,u,v;i<n;i++) u=rdn(),v=rdn(),ad2(u,v),ad2(v,u); int Q=rdn(); for(int i=1;i<=Q;i++) {int d=rdn(); lm[d]=rdn();} val=lm[s0]; he=tl=0; q[++tl]=s0; ini_dfs(s0,0); while(he<tl) { int k=q[++he]; tmp=lm[k]; tot++; dfs(k); if(tmp<0){fg=1;break;} add(S,tot,tmp,0); } if(fg)return; memset(lm,0,sizeof lm); Q=rdn(); for(int i=1;i<=Q;i++) {int d=rdn(); lm[d]=rdn();} he=tl=0; q[++tl]=s1; fa[s1]=0; ini_dfs2(s1,0); while(he<tl) { int k=q[++he]; tmp=lm[k]; tot++; dfs2(k); if(tmp<0){fg=1;break;} add(tot,T,tmp,0); } if(val!=lm[s1])fg=1; } } bool spfa() { memset(dis,-4,sizeof dis); dis[S]=0; info[S]=INF; info[T]=0; q.push(S); ins[S]=1; while(q.size()) { int k=q.front(); q.pop(); ins[k]=0; for(int i=hd[k],v;i;i=nxt[i]) if(cap[i]&&dis[v=to[i]]<dis[k]+w[i]) { dis[v]=dis[k]+w[i]; pre[v]=i; info[v]=Mn(info[k],cap[i]); if(!ins[v])ins[v]=1,q.push(v); } } return info[T]; } void ek() { int k=info[T]; flow+=k; for(int i=pre[T];i;i=pre[to[i^1]]) { cap[i]-=k; cap[i^1]+=k; ans+=k*w[i];} } int main() { freopen("w.in","r",stdin); freopen("w.out","w",stdout); tot=n=rdn(); S=0; T=++tot; I::solve(); if(fg){puts("-1");return 0;} while(spfa())ek(); if(flow==val)printf("%d\n",ans); else puts("-1"); return 0; }
第二题:http://codeforces.com/contest/1045/problem/H
考场上不会做……
#include<cstdio> #include<cstring> #include<algorithm> #include<map> using namespace std; const int N=105,mod=1e9+7; int upt(int x){while(x>=mod)x-=mod;while(x<0)x+=mod;return x;} int n,m,A,B,C,D,ans;char s1[N],s2[N],s[N]; struct Node{ int x,y; Node(int x=0,int y=0):x(x),y(y) {} bool operator< (const Node &b)const {return x==b.x?y<b.y:x<b.x;} }; map<Node,int> dp[N][2][N][N],mp[N][2][N][N]; void init() { memcpy(s,s1,sizeof s1); for(int i=1;i<=n;i++) if(s[i]=='1'){s[i]='0';break;} else s[i]='1'; } int dfs(int t,bool fx,int a,int b,int c,int d) { if(a<0||b<0||c<0||d<0)return 0; Node cr=Node(c,d); if(dp[t][fx][a][b].count(cr)) { return dp[t][fx][a][b][cr]; } if(!t) return dp[t][fx][a][b][cr]=((!a)&&(!b)&&(!c)&&(!d)); int ret=0; if(!fx) { ret=upt(ret+dfs(t-1,0,a-1,b,c,d)); ret=upt(ret+dfs(t-1,1,a,b-1,c,d)); } else { ret=upt(ret+dfs(t-1,0,a,b,c-1,d)); ret=upt(ret+dfs(t-1,1,a,b,c,d-1)); } dp[t][fx][a][b][cr]=ret; return ret; } int dfsx(int t,bool fx,int a,int b,int c,int d) { if(a<0||b<0||c<0||d<0)return 0; Node cr=Node(c,d); if(mp[t][fx][a][b].count(cr)) { return mp[t][fx][a][b][cr]; } if(!t) { int ret=((!a)&&(!b)&&(!c)&&(!d)); return mp[t][fx][a][b][cr]=ret; } if(s[t]=='0') { if(!fx)a--; else c--; int ret=dfsx(t-1,0,a,b,c,d); return mp[t][fx][a][b][cr]=ret; } int ta=a,tb=b,tc=c,td=d; if(!fx)tb--; else td--; int ret=dfsx(t-1,1,ta,tb,tc,td); ta=a; tb=b; tc=c; td=d; if(!fx)a--; else c--; ret=upt(ret+dfs(t-1,0,a,b,c,d));// mp[t][fx][a][b][cr]=ret; return ret; } void solve(int lm) { for(int t=0;t<=lm;t++) for(int fx=0;fx<=1;fx++) for(int i=0;i<=A;i++) for(int j=0;j<=B;j++) { dp[t][fx][i][j].clear(); mp[t][fx][i][j].clear(); } for(int t=1;t<lm;t++) { ans=upt(ans+dfs(t-1,1,A,B,C,D)); } ans=upt(ans+dfsx(lm-1,1,A,B,C,D)); } int main() { freopen("i.in","r",stdin); freopen("i.out","w",stdout); scanf("%s",s1+1); scanf("%s",s2+1); n=strlen(s1+1); m=strlen(s2+1); reverse(s1+1,s1+n+1); reverse(s2+1,s2+m+1); scanf("%d%d%d%d",&A,&B,&C,&D); init(); solve(n); ans=upt(-ans); memcpy(s,s2,sizeof s2); solve(m); printf("%d\n",ans); return 0; }
后来得知 “随便填” 的方案可以用组合数算。就是通过 01 和 10 的个数可以知道 0 和 1 分成了几段。
仔细一想,直接搜,不用记忆化就行。
不知为何自己的代码必须特判 if( B<C-1 || B>C ) puts(" 0 ");
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; int Mx(int a,int b){return a>b?a:b;} int Mn(int a,int b){return a<b?a:b;} const int N=1e5+5,mod=1e9+7; int upt(int x){while(x>=mod)x-=mod;while(x<0)x+=mod;return x;} int pw(int x,int k) {int ret=1;while(k){if(k&1)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=1;}return ret;} int n,A,B,C,D,jc[N],jcn[N]; char s[N],s1[N],s2[N]; struct Node{ int a,b,c,d; Node(int a=0,int b=0,int c=0,int d=0):a(a),b(b),c(c),d(d) {} bool operator< (const Node &v)const { if(a!=v.a)return a<v.a; if(b!=v.b)return b<v.b; if(c!=v.c)return c<v.c; return d<v.d; } }; void init() { memcpy(s,s1,sizeof s1); for(int i=1;i<=n;i++) if(s[i]=='0')s[i]='1'; else {s[i]='0';if(i==n)n--;break;} int lm=1e5;//not Mx(A,B,C,D) for *2 jc[0]=1;for(int i=1;i<=lm;i++)jc[i]=(ll)jc[i-1]*i%mod; jcn[lm]=pw(jc[lm],mod-2); for(int i=lm-1;i>=0;i--)jcn[i]=(ll)jcn[i+1]*(i+1)%mod; } int C2(int n,int m) { if(n==(-1)&&m==(-1))return 1;/// if(n<0||m<0||n<m)return 0; return (ll)jc[n]*jcn[m]%mod*jcn[n-m]%mod; } int cal(int t,bool fx,int a,int b,int c,int d) { if(a<0||b<0||c<0||d<0)return 0;// if(!t) return ((!a)&&(!b)&&(!c)&&(!d)); int t0,t1,c0,c1; if(!fx)c0=c+1,c1=b; else c0=c,c1=b+1; if(!fx)t0=a+c0-1,t1=d+c1; else t0=a+c0,t1=d+c1-1; int ret=0; if(t0+t1==t) ret=(ll)C2(t0-1,c0-1)*C2(t1-1,c1-1)%mod; c0=c; c1=b; t0=a+c0; t1=d+c1; if(t0+t1==t) ret=(ret+(ll)C2(t0-1,c0-1)*C2(t1-1,c1-1))%mod; return ret; } int dfs(int t,bool fx,int a,int b,int c,int d) { if(a<0||b<0||c<0||d<0)return 0; if(!t) return ((!a)&&(!b)&&(!c)&&(!d)); int ta=a,tb=b,tc=c,td=d; if(s[t]=='0') { if(!fx)ta--; else tc--; int ret=dfs(t-1,0,ta,tb,tc,td); return ret; } if(!fx)ta--; else tc--; int ret=cal(t-1,0,ta,tb,tc,td); ta=a; tc=c; if(!fx)tb--; else td--; ret=upt(ret+dfs(t-1,1,ta,tb,tc,td)); return ret; } int solve() { int ret=0; for(int t=1;t<n;t++) ret=upt(ret+cal(t-1,1,A,B,C,D)); if(n)ret=upt(ret+dfs(n-1,1,A,B,C,D));//if return ret; } int main() { scanf("%s",s1+1); scanf("%s",s2+1); scanf("%d%d%d%d",&A,&B,&C,&D); if(B<C-1||B>C){puts("0");return 0;}///? n=strlen(s1+1); reverse(s1+1,s1+n+1); init(); int ans=upt(-solve()); n=strlen(s2+1); reverse(s2+1,s2+n+1); memcpy(s,s2,sizeof s2); ans=upt(ans+solve()); printf("%d\n",ans); return 0; }
第三题:http://codeforces.com/contest/780/problem/H
题意: 给出 n 个点的坐标,第 i 个点和第 i%n+1 个点连线组成一个环,有 m 个动点在环上移动,满足每个动点的速度相同、相邻动点距离相等。
求一个最小的 R,满足存在一个时刻,任意相邻点的距离 <= R 。
学习了 #52224787(by Idxoi) 的 AC 代码。
考虑把 “第 i 个点和第 i%m+1 个点的距离” 随时间变化的函数图象画出来。如果两个点分别在两条线上,那么这个图象是一个二次函数。整个图象就是 O(n) 段二次函数接起来。
考虑二分答案,那么图象中 y 在 mid 以下部分的 x 都是合法的;该 mid 合法满足存在一个 x ,使得 x , x+T/m , x+2*T/m , ... x+m*T/m 都是合法的( T 是总长度;即等距的 m 个位置都是合法的)。
已知 mid ,对于一段二次函数,易求其在 y=mid 一下部分的 x ,且对于一个二次函数这样的 x 是0/1/2个区间;
所以把整个图象分成各个二次函数,然后求出一些合法的 x 区间;再考虑把 T 分成 [ 0 , T/m ] , [ T/m , 2*T/m ] , ... , [ (m-1)*T/m , m*T/m ] ,然后把这 m 个区间里的 “合法 x 区间集合” 都叠起来,如果有一个位置被覆盖了 m 次,那么该 mid 合法。
所以把整个图象分成若干段,满足每段是一个二次函数,且每段只在一个 [ 0 , T/m ] , ... , [ (m-1)*T/m , m*T/m ] 这样的区间里;然后对每段求一下合法的 x 区间,用差分的形式记录下来,按位置排序后扫一遍看看有没有被覆盖 m 次的位置即可。
注意这个排序是以位置为第一关键字、+1/-1为第二关键字;且要先减后加!
注意虽然算的是至多 n*4 段(n*2 个端点,每个区间被 m 的限制切一次,总数再 *2),但空间要再开大。不太知道原因……可能是精度使得多出了一些段?
注意二分不要像平常一样写。 mid 是否合法只是影响二分方向,最终的答案不一定是一个合法的 mid ,而是最后二分到的位置。可能是因为精度才这样的?
找函数图象的实现方法就是把给出的 n 条线段的长度、方向都预处理出来,然后两个指针卡着两个点 st , en 的位置,每次走到 “ 下一个 [ 0 , T/m ] 这样的段的末端 ”、“ st 所在线段的末端 ” 、 “ en 所在线段的末端 ” 中较近的一个。就是看看哪个长度短,然后 st 、en 就移动到 “当前位置 + 长度×所在线段方向” 的位置。
已知两动点 st , en 的当前位置以及移动方向向量(就是所在线段的方向),如何求出距离的二次函数方程?
考虑 \( st=(a_1+c_1*x , b_1+d_1*x ) , en=(a_2+c_2*x , b_2+d_2*x ) \) (即,\( (a_1,b_1) \) 就是 st 的初始位置, \( (c_1,d_1) \) 是移动方向, x 是时间自变量)
\( dis^2 = ( (a_1+c_1*x) - (a_2+c_2*x) )^2 + ( ( b_1+d_1*x ) - ( b_2+d_2*x ) )^2 \)(就是平常的 \( dis^2=(x_1-x_2)^2+(y_1-y_2)^2 \))
\( dis^2 = ( (c_1-c_2)^2+(d_1-d_2)^2 )x^2 + 2[ (a_1-a_2)(c_1-c_2) + (b_1-b_2)(d_1-d_2) ]x + (a_1-a_2)^2 + (b_1-b_2)^2 \)
所以可知关于 \(dis^2\) 的二次函数 \( y=a*x^2+b*x+c \) 的系数。要判断 dis<=mid 的部分,就是判断 \(dis^2<=mid^2\) 的部分。
注意到二次函数的 a 是 >=0 的,所以可以少讨论一点。
注意在求交点的时候判断正负就不要加 eps 了。
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define db long double using namespace std; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } const int N=(3e4+5)*10; const db eps=1e-9; int dcmp(db x){return (x>-eps)-(x<eps);} db Sqr(db x){return x*x;} int n,m,tot,cnt; db len[N]; struct Node{ db x,y; Node(db x=0,db y=0):x(x),y(y) {} Node operator+ (const Node &b)const{return Node(x+b.x,y+b.y);} Node operator- (const Node &b)const{return Node(x-b.x,y-b.y);} Node operator* (const db &b)const{return Node(x*b,y*b);} Node operator/ (const db &b)const{return Node(x/b,y/b);} db mod(){ return Sqr(x)+Sqr(y);} }a[N],dir[N]; db dot(Node u,Node v){return u.x*v.x+u.y*v.y;} struct Dt{ db ql,qr,a,b,c; Dt(db l=0,db r=0,db a=0,db b=0,db c=0):ql(l),qr(r),a(a),b(b),c(c) {} }h[N]; struct F{ db p;int v; F(db p=0,int v=0):p(p),v(v) {} bool operator< (const F &b)const {return dcmp(p-b.p)==0?v<b.v:p<b.p;}//v<b.v not v>b.v }tp[N<<1]; db chk(db mid) { cnt=0; for(int i=1;i<=tot;i++) { db a=h[i].a,b=h[i].b,c=h[i].c-mid;//a>=0 db ql=h[i].ql,qr=h[i].qr; if(a<eps) { if(dcmp(b)==0) { if(c<eps) {tp[++cnt]=F(ql,1);tp[++cnt]=F(qr,-1);} } else { db x=-c/b; if(b>0&&x>0) { tp[++cnt]=F(ql,1); tp[++cnt]=F(min(ql+x,qr),-1); } else if(b<0&&ql+x<qr) { tp[++cnt]=F(ql+max(x,(db)0),1); tp[++cnt]=F(qr,-1); } } } else { db dlt=Sqr(b)-4*a*c; if(dlt<0)continue; dlt=sqrt(dlt); db tmp=2*a; db x0=ql+(-b-dlt)/tmp, x1=ql+(-b+dlt)/tmp; x0=max(x0,ql); x1=min(x1,qr); if(x0<x1) { tp[++cnt]=F(x0,1);tp[++cnt]=F(x1,-1); } } } sort(tp+1,tp+cnt+1); for(int i=1,lj=0;i<=cnt;i++) { lj+=tp[i].v; if(lj==m)return true;} return false; } void cz(int &x){ x++; if(x>n)x=1;} int main() { n=rdn();m=rdn(); for(int i=1;i<=n;i++)a[i].x=rdn(),a[i].y=rdn(); a[n+1]=a[1]; db sm=0; for(int i=1;i<=n;i++) { len[i]=sqrt((a[i+1]-a[i]).mod()); sm+=len[i]; dir[i]=(a[i+1]-a[i])/len[i]; } db ave=sm/m,L=len[1],R=ave,nw=0; int id=1;//nw=0 not ave int pl=1,pr=1; for(;dcmp(R-len[pr])>=0;pr++)R-=len[pr]; Node st=a[1],en=a[pr]+dir[pr]*R; R=len[pr]-R; while(dcmp(sm-nw)>0) { db cr=min(id*ave-nw,min(L,R)); Node d0=en-st,d1=dir[pr]-dir[pl]; st=st+dir[pl]*cr; en=en+dir[pr]*cr;//after d0,d1 h[++tot]=Dt(nw-(id-1)*ave,nw-(id-1)*ave+cr, d1.mod(),2*dot(d0,d1),d0.mod()); L-=cr; if(dcmp(L)==0){cz(pl);st=a[pl];L=len[pl];} R-=cr; if(dcmp(R)==0){cz(pr);en=a[pr];R=len[pr];} //cz(pl) not ++pl nw+=cr; if(dcmp(id*ave-nw)==0)id++;//<= not == } db l=0,r=ave; while(r-l>eps) { db mid=(l+r)/2; if(chk(Sqr(mid)))r=mid; else l=mid; } printf("%.10Lf\n",l);//l not ans return 0; }