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;//
}//

  

posted @ 2016-12-07 22:18  Claris  阅读(1483)  评论(0编辑  收藏  举报