Asia Hong Kong Regional Contest 2016
A. Colourful Graph
可以在$2n$步之内实现交换任意两个点的颜色,然后就可以构造出方案。
#include <bits/stdc++.h> using namespace std ; typedef long long LL; const int mod=1e9+7,Maxn=222; const LL Inf=1LL<<60; int n,m,K; int col[Maxn],col2[Maxn]; int done[Maxn],pre[Maxn]; vector<int>G[Maxn]; vector<int> way[102][102];//i->j int fst[222]; void bfs(int st){ for(int i=1;i<=n;i++)done[i]=i==st?1:0; queue<int>q; q.push(st); while(!q.empty()){ int u=q.front();q.pop(); for(int i=0;i<G[u].size();i++){ int v=G[u][i];if(done[v])continue; done[v]=1; pre[v]=u; q.push(v); } } for(int i=1;i<=n;i++){ int u=i; way[st][i].clear(); for(;u!=st;u=pre[u])way[st][i].push_back(u); way[st][i].push_back(st); reverse(way[st][i].begin(),way[st][i].end()); } } void pt(){ for(int i=1;i<=n;i++)printf("%d%c",col[i],i==n?'\n':' '); } void swp(int st,int ed,int ty){ vector<int>tmp=way[st][ed]; for(int i=0;i<tmp.size()-1;i++){ if(i==tmp.size()-2){ if(ty)col[tmp[i+1]]=col[tmp[i]]; else swap(col[tmp[i+1]],col[tmp[i]]); } else swap(col[tmp[i]],col[tmp[i+1]]); pt(); } for(int i=tmp.size()-2;i>0;i--){ swap(col[tmp[i]],col[tmp[i-1]]); pt(); } } void solve () { for(int i=1;i<=n;i++)scanf("%d",col+i),G[i].clear(); for(int i=1;i<=n;i++)scanf("%d",col2+i); for(int i=0;i<m;i++){ int u,v;scanf("%d%d",&u,&v); G[u].push_back(v); G[v].push_back(u); } //puts("ok"); bool flag=1; for(int i=1;i<=n;i++){ int t=0; for(int j=1;j<=n;j++)if(col2[i]==col[j]){t=1;break;} if(!t){flag=0;break;} } if(!flag){ puts("Impossible"); return; } pt(); for(int i=1;i<=n;i++)bfs(i); memset(fst,0,sizeof fst); for(int i=1;i<=n;i++){ if(!fst[col2[i]]){ fst[col2[i]]=i; if(col[i]==col2[i])continue; for(int j=1;j<=n;j++){ if(col[j]==col2[i]){ swp(j,i,0); break; } } } } for(int i=1;i<=n;i++){ if(col[i]!=col2[i])swp(fst[col2[i]],i,1); } } int main () { while ( ~scanf ( "%d%d%d" , &n,&m,&K ) ) solve () ; return 0 ; }
B. Doors
答案就是这些折线之间距离的最小值除以2。
#include<cstdio> #include<cmath> #include<algorithm> using namespace std; double R,L,W,Alpha,Beta; int Case; const double pi=acos(-1.0),eps=1e-9,inf=10000.0; int sgn(double x){ if(x<-eps)return -1; if(x>eps)return 1; return 0; } struct P{ double x,y; P(){x=y=0;} P(double _x,double _y){x=_x,y=_y;} P operator+(P v){return P(x+v.x,y+v.y);} P operator-(P v){return P(x-v.x,y-v.y);} double operator*(P v){return x*v.x+y*v.y;} double len(){return hypot(x,y);} }; double cross(P a,P b){return a.x*b.y-a.y*b.x;} double dist_point_to_segment(P p,P a,P b){ if(sgn((p-a)*(b-a))>=0&&sgn((p-b)*(a-b))>=0) return fabs(cross(p-a,b-a))/(b-a).len(); return min((p-a).len(),(p-b).len()); } double cal(P a,P b,P c,P d){ return min(min(dist_point_to_segment(a,c,d), dist_point_to_segment(b,c,d)), min(dist_point_to_segment(c,a,b), dist_point_to_segment(d,a,b))); } double solve(){ P A(-inf,W),B(0,W); P D(L,W),E(inf,W); P G(L,0),H(inf,0); Alpha=pi-Alpha,Beta=pi-Beta; P C=D+P(cos(Alpha)*L,sin(Alpha)*L); P F=G+P(cos(Beta)*L,sin(Beta)*L); double ans=min(L,W); ans=min(ans,cal(A,B,C,D)); ans=min(ans,cal(C,D,F,G)); ans=min(ans,cal(D,E,F,G)); ans/=2.0; ans=min(ans,R); ans=max(ans,0.0); return ans; } int main(){ scanf("%lf%lf%lf",&R,&L,&W); scanf("%d",&Case); while(Case--){ scanf("%lf%lf",&Alpha,&Beta); printf("%.9f\n",solve()); } return 0; }
C. Peak Tower
求出所有线段相交的时刻,在相邻时刻里三分答案即可。时间复杂度$O(n^4\log n)$。
#include<cstdio> #include<cstdlib> #include<cmath> #include<vector> #include<cstring> #include<algorithm> using namespace std; const int N=2000; const double eps=1e-9; double W,H,E,ans=1e9; int n,i,j; struct Rec{ double w,h,sx,sy,vx,vy; void read(){ scanf("%lf%lf%lf%lf%lf%lf",&w,&h,&sx,&sy,&vx,&vy); } }a[N]; int sgn(double x){ if(x<-eps)return -1; if(x>eps)return 1; return 0; } int m,cnt; double vipy[N]; int cov[N]; double ct[1000000]; int cntt; struct Ev{ double x,l,r;int p; Ev(){} Ev(double _x,double _l,double _r,int _p){x=_x,l=_l,r=_r,p=_p;} }e[N]; inline bool cmp(const Ev&a,const Ev&b){ return a.x<b.x; } double cal(double Time){ //if(Time<-eps||Time>E+eps)return; int i,j; m=0; for(i=1;i<=n;i++){ double xl=a[i].sx+a[i].vx*Time, xr=xl+a[i].w, yl=a[i].sy+a[i].vy*Time, yr=yl+a[i].h; xl=min(max(xl,0.0),W); xr=min(max(xr,0.0),W); yl=min(max(yl,0.0),H); yr=min(max(yr,0.0),H); //printf("%.8f %.8f %.8f %.8f\n",xl,xr,yl,yr); if(xl+eps>xr||yl+eps>yr)continue; vipy[++m]=yl; e[m]=Ev(xl,yl,yr,1); vipy[++m]=yr; e[m]=Ev(xr,yl,yr,-1); } double ret=0; if(!m){ //printf("fuck %.8f %.8f\n",Time,0.0); ans=0; return 0; } sort(vipy+1,vipy+m+1); sort(e+1,e+m+1,cmp); for(i=1;i<m;i++)cov[i]=0; for(i=1;i<=m;i++){ if(i>1){ for(j=1;j<m;j++){ if(cov[j]>0)ret+=(vipy[j+1]-vipy[j])*(e[i].x-e[i-1].x); } } if(ret>ans)return ret; double l=e[i].l,r=e[i].r; //printf("ev %.8f %.8f %.8f %d\n",e[i].x,l,r,e[i].p); for(j=1;j<m;j++){ double A=vipy[j],B=vipy[j+1]; if(sgn(A-l)>=0&&sgn(B-r)<=0){ cov[j]+=e[i].p; } } } //printf("%.8f %.8f\n",Time,ret); ans=min(ans,ret); return ret; } inline bool check(double Time){ if(Time<-eps||Time>E+eps)return 0; return 1; } void deal(double A,double B,double C,double D){ A-=C; B-=D; A=-A; if(!sgn(B))return; if(check(A/B))ct[++cntt]=A/B; } void search(double l,double r){ cal(r); double m1,m2,s1,s2; int step=0; while(l+1e-5<r&&step++<10){ m1=l+(r-l)/3.0; m2=r-(r-l)/3.0; s1=cal(m1); s2=cal(m2); if(s1<s2)r=m2-eps;else l=m1+eps; } } int main(){ scanf("%d%lf%lf%lf",&n,&W,&H,&E); for(i=1;i<=n;i++)a[i].read(); ct[++cntt]=0; ct[++cntt]=E; a[n+1].w=W; a[n+1].h=H; for(i=1;i<=n+1;i++) for(j=i+1;j<=n+1;j++){ deal(a[i].sx ,a[i].vx,a[j].sx ,a[j].vx); deal(a[i].sx+a[i].w,a[i].vx,a[j].sx ,a[j].vx); deal(a[i].sx ,a[i].vx,a[j].sx+a[j].w,a[j].vx); deal(a[i].sx+a[i].w,a[i].vx,a[j].sx+a[j].w,a[j].vx); deal(a[i].sy ,a[i].vy,a[j].sy ,a[j].vy); deal(a[i].sy+a[i].h,a[i].vy,a[j].sy ,a[j].vy); deal(a[i].sy ,a[i].vy,a[j].sy+a[j].h,a[j].vy); deal(a[i].sy+a[i].h,a[i].vy,a[j].sy+a[j].h,a[j].vy); } //cal(5); sort(ct+1,ct+cntt+1); cal(0); for(i=1;i<=cntt;i++)if(ct[i]>ct[i-1]+eps){ search(ct[i-1],ct[i]); } printf("%.10f",ans); }
D. Peak Tram
每个位置最多$O(n^2)$种可能的高度,然后DP即可。
#include <bits/stdc++.h> using namespace std ; typedef long long LL; const int mod=1e9+7,Maxn=71; const LL Inf=1LL<<60; vector<LL>V; LL p[Maxn],c[Maxn]; LL dp[2][142*71][71]; LL pre[2][142*71][71]; int getid(LL x){ return lower_bound(V.begin(),V.end(),x)-V.begin(); } int n,K; int tot; void init(int cs){ for(int i=0;i<tot;i++){ for(int j=0;j<=K;j++)dp[cs][i][j]=Inf; } } void calpre(int cs){ for(int i=0;i<tot;i++){ for(int j=0;j<=K;j++){ pre[cs][i][j]=dp[cs][i][j]; if(i)pre[cs][i][j]=min(pre[cs][i-1][j],pre[cs][i][j]); } } } inline void up(LL &x,LL y){if(x>y)x=y;} void solve () { V.clear(); LL tp=0; V.push_back(tp); for(int i=1;i<=n;i++){ scanf("%lld%lld",p+i,c+i); for(int j=-n;j<=n;j++)if(p[i]+j>0)V.push_back(p[i]+j); } sort(V.begin(),V.end()); V.erase(unique(V.begin(),V.end()),V.end()); tot=V.size(); int cs=0; init(cs); dp[cs][0][0]=0; calpre(cs); for(int i=1;i<=n;i++){ init(cs^1); //bubeikanjian int tid=getid(p[i]); for(int j=1;j<tot;j++){ LL cost=0; if(j<tid){ cost=abs(V[j]-p[i])*c[i]; } for(int k=0;k<=K;k++){ up(dp[cs^1][j][k],dp[cs][j][k]+cost); } } //kanjian for(int j=1;j<tot;j++){ LL cost=abs(V[j]-p[i])*c[i]; for(int k=1;k<=K;k++){ up(dp[cs^1][j][k],pre[cs][j-1][k-1]+cost); up(dp[cs^1][j][k],pre[cs][j-1][k]+cost); } } calpre(cs^1); cs^=1; } LL ans=pre[cs][tot-1][K]; printf("%lld\n",ans); } int main () { while ( ~scanf ( "%d%d" , &n,&K ) ) solve () ; return 0 ; }
E. Perfect k-ary Tree
换根树形DP,需要记录树的高度,但是因为是满$k$叉树,因此树高是$O(\log n)$级别的。
#include <bits/stdc++.h> using namespace std ; typedef long long LL; const int mod=1e9+7,Maxn=100020; int dp1[19][Maxn]; int n,K; vector<int>G[Maxn]; int f[2][6]; inline void up(int &x,int y){x+=y;if(x>=mod)x-=mod;} void dfs1(int u,int p){ for(int i=0;i<G[u].size();i++){ int v=G[u][i];if(v==p)continue; dfs1(v,u); } dp1[0][u]=1; //printf("u=%d\n",u); for(int i=1;(1<<i)<=n;i++){ int cs=0; memset(f[cs],0,sizeof f[cs]); f[cs][0]=1; for(int j=0;j<G[u].size();j++){ int v=G[u][j];if(v==p)continue; memset(f[cs^1],0,sizeof f[cs^1]); for(int k=0;k<=K;k++){ if(!f[cs][k])continue; if(k<K)up(f[cs^1][k+1],1LL*f[cs][k]*dp1[i-1][v]%mod); up(f[cs^1][k],f[cs][k]); } cs^=1; } dp1[i][u]=f[cs][K]; } //for(int i=0;(1<<i)<=n;i++)printf("%d ",dp1[i][u]);puts(""); } int dp2[19][Maxn]; int pre[Maxn][6],suf[Maxn][6]; int ans; void dfs2(int u,int p){ for(int h=1;(1<<h)<=n;h++){ //suanqianhouzhui memset(pre[0],0,sizeof pre[0]); pre[0][0]=1; for(int i=0;i<G[u].size();i++){ int v=G[u][i];if(v==p){memcpy(pre[i+1],pre[i],sizeof pre[i]);continue;} memset(pre[i+1],0,sizeof pre[i+1]); for(int j=0;j<=K;j++){ up(pre[i+1][j],pre[i][j]); if(j<K)up(pre[i+1][j+1],1LL*dp1[h-1][v]*pre[i][j]%mod); } } int lst=G[u].size(); memset(suf[lst],0,sizeof suf[lst]); suf[lst][1]=dp2[h-1][u]; suf[lst][0]=1; for(int i=G[u].size()-1;i>=0;i--){ int v=G[u][i];if(v==p){memcpy(suf[i],suf[i+1],sizeof suf[i+1]);continue;} memset(suf[i],0,sizeof suf[i]); for(int j=0;j<=K;j++){ up(suf[i][j],suf[i+1][j]); if(j<K)up(suf[i][j+1],1LL*dp1[h-1][v]*suf[i+1][j]%mod); } /* if(h==2&&u==2){ printf("v=%d\n",v); for(int j=0;j<=K;j++)printf("",suf); } */ } /* if(u==2){ puts("haha"); for(int i=0;i<=G[u].size();i++){ for(int j=0;j<=K;j++)printf("%d ",suf[i][j]);puts(""); } } */ for(int i=0;i<G[u].size();i++){ int v=G[u][i];if(v==p)continue; dp2[h][v]=0; for(int j=0;j<=K;j++){ up(dp2[h][v],1LL*pre[i][j]*suf[i+1][K-j]%mod); } } /* if(suf[0][K]){ printf("u=%d h=%d suf=%d\n",u,h,suf[0][K]); } */ up(ans,suf[0][K]); } for(int i=0;i<G[u].size();i++){ int v=G[u][i];if(v==p)continue; dp2[0][v]=1; dfs2(v,u); } } void solve () { for(int i=1;i<=n;i++)G[i].clear(); for(int i=1;i<n;i++){ int u,v;scanf("%d%d",&u,&v); G[u].push_back(v); G[v].push_back(u); } ans=n; dfs1(1,0); dfs2(1,0); printf("%d\n",ans); } int main () { while ( ~scanf ( "%d%d" , &n,&K ) ) solve () ; return 0 ; }
F. Playing with Numbers
枚举答案中2和3的幂次来自哪里即可。
#include <bits/stdc++.h> using namespace std ; const int MAXN = 50005 ; int a[MAXN] , b[MAXN] ; int prea[MAXN][2] , preb[MAXN][2] ; int sufa[MAXN][2] , sufb[MAXN][2] ; int n ; double getlog ( int x , int y ) { return x * log ( 2.0 ) + y * log ( 3.0 ) ; } void getlcm () { int ansa = 0 , ansb = 0 ; for ( int i = 1 ; i <= n ; ++ i ) { int tmpa = min ( prea[i - 1][1] , sufa[i + 1][1] ) ; int tmpb = min ( preb[i - 1][1] , sufb[i + 1][1] ) ; tmpa = max ( tmpa , a[i] ) ; tmpb = max ( tmpb , b[i] ) ; if ( getlog ( tmpa , tmpb ) > getlog ( ansa , ansb ) ) { ansa = tmpa ; ansb = tmpb ; } } printf ( "%d %d" , ansa , ansb ) ; } void getgcd () { int ansa = 1000 , ansb = 1000 ; for ( int i = 1 ; i <= n ; ++ i ) { int tmpa = max ( prea[i - 1][0] , sufa[i + 1][0] ) ; int tmpb = max ( preb[i - 1][0] , sufb[i + 1][0] ) ; tmpa = min ( tmpa , a[i] ) ; tmpb = min ( tmpb , b[i] ) ; if ( getlog ( tmpa , tmpb ) < getlog ( ansa , ansb ) ) { ansa = tmpa ; ansb = tmpb ; } } printf ( "%d %d" , ansa , ansb ) ; } void solve () { for ( int i = 1 ; i <= n ; ++ i ) { scanf ( "%d%d" , &a[i] , &b[i] ) ; } if ( n == 1 ) { printf ( "%d %d %d %d\n" , a[1] , b[1] , a[1] , b[1] ) ; return ; } if ( n == 2 ) { if ( a[1] < a[2] ) swap ( a[1] , a[2] ) ; if ( b[1] < b[2] ) swap ( b[1] , b[2] ) ; printf ( "%d %d %d %d\n" , a[1] , b[1] , a[1] , b[1] ) ; printf ( "%d %d %d %d\n" , a[2] , b[2] , a[2] , b[2] ) ; return ; } prea[0][0] = preb[0][0] = 0 ; prea[0][1] = preb[0][1] = 1000 ; sufa[n + 1][0] = sufb[n + 1][0] = 0 ; sufa[n + 1][1] = sufb[n + 1][1] = 1000 ; for ( int i = 1 ; i <= n ; ++ i ) { prea[i][0] = max ( prea[i - 1][0] , a[i] ) ; preb[i][0] = max ( preb[i - 1][0] , b[i] ) ; prea[i][1] = min ( prea[i - 1][1] , a[i] ) ; preb[i][1] = min ( preb[i - 1][1] , b[i] ) ; } for ( int i = n ; i >= 1 ; -- i ) { sufa[i][0] = max ( sufa[i + 1][0] , a[i] ) ; sufb[i][0] = max ( sufb[i + 1][0] , b[i] ) ; sufa[i][1] = min ( sufa[i + 1][1] , a[i] ) ; sufb[i][1] = min ( sufb[i + 1][1] , b[i] ) ; } for ( int i = 0 ; i < n ; ++ i ) { if ( i <= n - 3 ) { printf ( "%d %d" , prea[n][0] , preb[n][0] ) ; } else if ( i == n - 2 ) { getlcm () ; } else { printf ( "%d %d" , prea[n][1] , preb[n][1] ) ; } printf ( " " ) ; if ( i == 0 ) { printf ( "%d %d" , prea[n][0] , preb[n][0] ) ; } else if ( i == 1 ) { getgcd () ; } else { printf ( "%d %d" , prea[n][1] , preb[n][1] ) ; } puts ( "" ) ; } } int main () { while ( ~scanf ( "%d" , &n ) ) solve () ; return 0 ; }
G. Scaffolding
留坑。
H. Slim Cut
枚举割中最大的边,那么大于它的边都要保留,得到若干个连通块,那么需要把这些连通块划分给$S$或者$T$,使得点数尽量均衡,01背包即可。
用并查集维护连通块,那么就变成了带增删物品的01背包,在线段树上分治同时用bitset加速即可。
时间复杂度$O(\frac{n^2\log n}{64})$。
#include<cstdio> #include<cstring> #include<algorithm> #include<bitset> using namespace std; const int N=50010,M=140000,MAXE=3000000; typedef bitset<7010>BS; int n,m,i,j,x,y,f[N],size[N],id[N]; int pos,st[N],en[N],val[N],cnt; double ans=1e9; int gq[M],g[M],v[MAXE],nxt[MAXE],ed; BS base; struct E{ int x,y,w; }e[N]; inline bool cmp(const E&a,const E&b){return a.w>b.w;} int F(int x){return f[x]==x?x:f[x]=F(f[x]);} inline void add(int&x,int y){ v[++ed]=y; nxt[ed]=x; x=ed; } inline void addquery(int x){ add(gq[pos],x); } inline void merge(int x,int y){ x=F(x); y=F(y); if(x==y)return; en[id[x]]=pos; en[id[y]]=pos; pos++; cnt++; st[cnt]=pos; val[cnt]=size[x]+size[y]; size[y]+=size[x]; f[x]=y; id[y]=cnt; } void change(int x,int a,int b,int c,int d,int p){ if(c<=a&&b<=d){ add(g[x],p); return; } int mid=(a+b)>>1; if(c<=mid)change(x<<1,a,mid,c,d,p); if(d>mid)change(x<<1|1,mid+1,b,c,d,p); } void dfs(int x,int a,int b,BS dp,int cur){ for(int i=g[x];i;i=nxt[i])dp|=dp<<v[i],cur+=v[i]; if(a==b){ if(cur!=n)while(1); int now=-1; for(int i=n/2;i>=1;i--)if(dp[i]){ now=i; break; } if(now<1)return; for(int i=gq[a];i;i=nxt[i]){ ans=min(ans,1.0*v[i]/now); } return; } int mid=(a+b)>>1; dfs(x<<1,a,mid,dp,cur); dfs(x<<1|1,mid+1,b,dp,cur); } int main(){ scanf("%d%d",&n,&m); for(i=1;i<=m;i++){ scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w); e[i].x++;e[i].y++; } sort(e+1,e+m+1,cmp); for(i=1;i<=n;i++){ f[i]=i;size[i]=1; id[i]=++cnt; val[i]=1; st[i]=1; } pos=1; for(i=1;i<=m;i=j){ addquery(e[i].w); for(j=i;j<=m&&e[i].w==e[j].w;j++){ merge(e[j].x,e[j].y); } } for(i=1;i<=cnt;i++)if(!en[i])en[i]=pos; for(i=1;i<=cnt;i++)change(1,1,pos,st[i],en[i],val[i]); base[0]=1; dfs(1,1,pos,base,0); printf("%.10f",ans); }
I. Special Tour
留坑。
J. Taboo
建立AC自动机,然后DP即可,发现环就返回无限解。
#include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> using namespace std; const int N=200010; const int inf=100000000; int n,i,j,x; char s[N]; int tot,son[N][2],ban[N],fail[N],q[N]; int f[N],vis[N],in[N]; void ins(){ scanf("%s",s); int l=strlen(s); int i,x=0,w; for(i=0;i<l;i++){ w=s[i]-'0'; if(!son[x][w])son[x][w]=++tot; x=son[x][w]; } ban[x]=1; } void make(){ int h=1,t=0,i,j,x; fail[0]=-1; for(i=0;i<2;i++)if(son[0][i])q[++t]=son[0][i]; while(h<=t){ for(x=q[h++],i=0;i<2;i++) if(son[x][i]){ fail[son[x][i]]=son[fail[x]][i]; q[++t]=son[x][i]; ban[son[x][i]]|=ban[fail[son[x][i]]]; }else{ son[x][i]=son[fail[x]][i]; } } } int dp(int x){ if(in[x]){ puts("-1"); exit(0); } if(vis[x])return f[x]; in[x]=1; vis[x]=1; f[x]=-inf; if(!ban[x]){ f[x]=0; for(int i=0;i<2;i++){ f[x]=max(f[x],dp(son[x][i])+1); } } in[x]=0; // printf("dp[%d]=%d\n",x,f[x]); return f[x]; } int main(){ scanf("%d",&n); while(n--)ins(); make(); dp(0); //printf("ans=%d\n",f[0]); for(x=0,i=1;i<=f[0];i++){ for(int j=0;j<2;j++){ if(f[son[x][j]]+1==f[x]){ x=son[x][j]; printf("%d",j); break; } } } puts(""); return 0; }
K. Team Up
将包含关系建树,同时将技能按dfs序重标号,那么每门课程都是一个区间。
考虑贪心,每次取出包含这个点的最小的区间即可。
用并查集维护每个点向上第一个还可以选人的节点即可。
#include<cstdio> #include<cstdlib> #include<cmath> #include<vector> #include<cstring> #include<algorithm> using namespace std; const int N=1500010; const int inf=100000000; int n,m,p,i,j,x,size[N],g[N],G[N],v[N],nxt[N],ed,a[N],son[N]; int from[N],father[N]; int st[N],en[N],dfn; int ans; int cnt; int f[N]; bool vis[N]; vector<int>fin[300010]; inline bool cmp(int x,int y){return size[x]>size[y];}// inline void add(int&x,int y){v[++ed]=y;nxt[ed]=x;x=ed;}// void dfs(int x){// for(int i=son[x];i;i=nxt[i])dfs(v[i]);// for(int i=g[x];i;i=nxt[i]){// // printf("dfn %d\n",v[i]); if(!vis[v[i]]){ from[++dfn]=x;// vis[v[i]]=1; } }// en[x]=dfn;// }// int ask(int x){// if(!x)return 0;// if(f[x]==x){// if(G[x])return x;// f[x]=father[x];// }// return f[x]=ask(f[x]);// }// void solve(){// while(1){// int now=1;// while(1){// //printf("->%d %d\n",now,from[now]); x=ask(from[now]);//return a class if(!x)return;// //if(!G[x])while(1); int y=v[G[x]];// G[x]=nxt[G[x]];// fin[ans+1].push_back(y);// now=en[x]+1;// if(now>n)break;// } ans++; } } int main(){ scanf("%d%d%d",&n,&m,&p);// for(i=1;i<=m;i++){// scanf("%d",&size[i]);// for(j=0;j<size[i];j++){// scanf("%d",&x);// add(g[i],x);// }// }//50W for(i=1;i<=p;i++){// scanf("%d",&x);// add(G[x],i);// }//30W for(i=1;i<=m;i++)a[i]=i;// sort(a+1,a+m+1,cmp);// for(i=1;i<=m;i++){// x=a[i];// father[x]=from[v[g[x]]];// for(j=g[x];j;j=nxt[j]){// from[v[j]]=x;// }// }// for(i=1;i<=n;i++)if(!from[i])return puts("0"),0;// for(i=1;i<=m;i++)if(father[i])add(son[father[i]],i);//30W for(i=1;i<=m;i++)if(!father[i])dfs(i);// //if(dfn<n)while(1); for(i=1;i<=m;i++)f[i]=i;// //for(i=1;i<=m;i++)printf("%d\n",father[i]); solve();// printf("%d\n",ans);// for(i=1;i<=ans;i++){// int t=fin[i].size();// printf("%d",t);// for(int j=0;j<t;j++)printf(" %d",fin[i][j]);// puts("");// }// return 0;// }//