zoukankan      html  css  js  c++  java
  • 2016-2017 National Taiwan University World Final Team Selection Contest (Codeforces Gym) 部分题解

     

    D

    考虑每个点被删除时其他点对它的贡献,然后发现要求出距离为1~k的点对有多少个。

    树分治+FFT。分治时把所有点放一起做一遍FFT,然后减去把每棵子树单独做FFT求出来的值。

    复杂度$nlog^2n$

    #include<bits/stdc++.h>
    #define N 270000
    #define pi acos(-1)
    #define ll long long
    #define inf 0x3f3f3f3f
    using namespace std;
    const int p = 1000000007;
    int pw(int x,int y)
    {
    	int lst=1;
    	while(y)
    	{
    		if(y&1)lst=1LL*lst*x%p;
    		y>>=1;
    		x=1LL*x*x%p;
    	}
    	return lst;
    }
    int head[N],ver[2*N],nxt[2*N],tot;
    void add(int a,int b)
    {
    	tot++;nxt[tot]=head[a];head[a]=tot;ver[tot]=b;return ;
    }
    struct E
    {
    	double x,y;
    	E(){;}
    	E(double _x,double _y)
    	{
    		x=_x;y=_y;
    	}
    	friend E operator + (E &a,E &b)
    	{
    		return E(a.x+b.x,a.y+b.y);
    	}
    	friend E operator - (E &a,E &b)
    	{
    		return E(a.x-b.x,a.y-b.y);
    	}
    	friend E operator * (E &a,E &b)
    	{
    		return E(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
    	}
    }a[N],b[N],c[N];
    int R[N];
    ll ans[N];
    void fft(E *a,int f,int n)
    {
    	for(int i=0;i<n;i++)if(i>R[i])swap(a[i],a[R[i]]);
    	for(int i=1;i<n;i<<=1)
    	{
    		E wn(cos(pi/i),f*sin(pi/i));
    		for(int j=0;j<n;j+=(i<<1))
    		{
    			E w(1,0);
    			for(int k=0;k<i;k++,w=w*wn)
    			{
    				E x=a[j+k],y=a[j+k+i]*w;
    				a[j+k]=x+y;a[j+k+i]=x-y;
    			}
    		}
    	}
    	if(f==-1)
    	{
    		for(int i=0;i<n;i++)a[i].x/=n;
    	}
    	return ;
    }
    void FFT(int *sa,int m,int f)
    {
    	int l=0,n=1;
    	while(n<=2*m)n<<=1,l++;
    	for(int i=0;i<n;i++)
    	{
    		a[i].y=a[i].x=b[i].x=b[i].y=0;
    		if(i<=m)a[i].x=b[i].x=sa[i];
    	}
    	for(int i=0;i<n;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(l-1));
    	fft(a,1,n);fft(b,1,n);
    	for(int i=0;i<n;i++)c[i]=a[i]*b[i];
    	fft(c,-1,n);
    	for(int i=2;i<n;i++)
    	{
    		ll tmp=(ll)(c[i].x+0.5);
    		ans[i-1]+=f*tmp;
    	}
    	return ;
    }
    int size[N],mn,id,sum,v[N];
    int n;
    void dfs(int x,int f)
    {
    	int mx=0;
    	size[x]=1;
    	for(int i=head[x];i;i=nxt[i])
    	{
    		if(v[ver[i]]||ver[i]==f)continue;
    		dfs(ver[i],x);
    		size[x]+=size[ver[i]];
    		mx=max(mx,size[ver[i]]);
    	}mx=max(mx,sum-size[x]);
    	if(mx<mn)
    	{
    		mn=mx;
    		id=x;
    	}
    	return ;
    }
    int now[N],nw[N];
    int mxx=0;
    void dffs(int x,int f,int dp)
    {
    	size[x]=1;ans[dp]+=2;now[dp]++,nw[dp]++;
    	if(dp>mxx)mxx=dp;
    	for(int i=head[x];i;i=nxt[i])
    	{
    		if(v[ver[i]]||ver[i]==f)continue;
    		dffs(ver[i],x,dp+1);
    		size[x]+=size[ver[i]];
    	}
    	return ;
    }
    void solve(int x)
    {
    	sum=size[x];mn=inf;id=x;
    	dfs(x,-1);
    	x=id;
    	v[x]=1;size[x]=1;ans[1]++;
    	for(int i=0;i<=sum;i++)now[i]=0;
    	int mx=0;
    	for(int i=head[x];i;i=nxt[i])
    	{
    		if(v[ver[i]])continue;
    		mxx=0;
    		dffs(ver[i],x,2);
    		size[x]+=size[ver[i]];
    		FFT(nw,mxx,-1);
    		mx=max(mx,mxx);
    		for(int j=0;j<=mxx;j++)nw[j]=0;
    	}
    	if(mx)FFT(now,mx,1);
    	for(int i=head[x];i;i=nxt[i])
    	{
    		if(!v[ver[i]])solve(ver[i]);
    	}
    }
    int main()
    {
    	scanf("%d",&n);
    	int t1,t2;
    	for(int i=1;i<n;i++)
    	{
    		scanf("%d%d",&t1,&t2);
    		add(t1,t2);add(t2,t1);
    	}
    	size[1]=n;
    	solve(1);
    	ll as=0;
    	for(int i=1;i<=n;i++)
    	{
    		ans[i]%=p;
    		as+=ans[i]*pw(i,p-2)%p;
    	}
    	as%=p;
    	for(int i=2;i<=n;i++)
    	{
    		as=as*i%p;
    	}
    	printf("%I64d
    ",as);
    	return 0;
    }
    

     

    E

    把每条线段看成二维平面上的一个点。

    相当于求从(0,0)点到(n+1,n+1)的一条权值和最小的一条路径,且相邻两个点之间不能有其他点。

    CDQ分治+单调栈+线段树

    和bzoj 4273很像。

    #include<bits/stdc++.h>
    #define N 100055
    #define inf 2147483647
    #define ls x<<1,l,mid
    #define rs x<<1|1,mid+1,r
    using namespace std;
    int n;
    int p[N],v[N];
    int a[N*4];
    void gai(int x,int l,int r,int pos,int z)
    {
    	if(l==r)
    	{
    		a[x]=z;return ;
    	}
    	int mid=(l+r)>>1;
    	if(pos<=mid)gai(ls,pos,z);
    	else gai(rs,pos,z);
    	a[x]=min(a[x<<1],a[x<<1|1]);
    	return ;
    }
    int qur(int x,int l,int r,int ll,int rr)
    {
    	if(l>=ll&&r<=rr)return a[x];
    	int mid=(l+r)>>1;
    	if(ll>mid)return qur(rs,ll,rr);
    	if(rr<=mid)return qur(ls,ll,rr);
    	return min(qur(rs,ll,rr),qur(ls,ll,rr));
    }
    int st1[N],top1,st2[N],top2;
    int q1[N],cnt1,q2[N],cnt2,f[N];
    bool cmp(int x,int y)
    {
    	return p[x]<p[y];
    }
    void solve(int l,int r)
    {
    	if(l==r)return ;
    	int mid=(l+r)>>1;
    	solve(l,mid);
    	top1=top2=cnt1=cnt2=0;
    	for(int i=l;i<=mid;i++)q1[++cnt1]=i;
    	for(int i=mid+1;i<=r;i++)q2[++cnt2]=i;
    	sort(q1+1,q1+cnt1+1,cmp);sort(q2+1,q2+cnt2+1,cmp);
    	int pt=1;
    	for(int i=1;i<=cnt2;i++)
    	{
    		while(pt<=cnt1&&p[q1[pt]]<=p[q2[i]])
    		{
    			while(top1&&st1[top1]<q1[pt])
    			{
    				gai(1,1,n,p[st1[top1]],inf);
    				top1--;
    			}
    			gai(1,1,n,p[q1[pt]],f[q1[pt]]+v[q1[pt]]);
    			st1[++top1]=q1[pt];
    			pt++;
    		}
    		while(top2&&st2[top2]>q2[i])top2--;
    		int tmp=0;
    		if(top2)tmp=p[st2[top2]]+1;
    		f[q2[i]]=min(f[q2[i]],qur(1,1,n,tmp,p[q2[i]]));
    		st2[++top2]=q2[i];
    	}
    	while(top1)gai(1,1,n,p[st1[top1]],inf),top1--;
    	solve(mid+1,r);
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)scanf("%d",&p[i]);
    	for(int i=1;i<=n;i++)scanf("%d",&v[i]);
    	for(int i=1;i<=4*(n+1);i++)a[i]=inf;
    	p[n+1]=n+1;p[0]=0;
    	memset(f,0x3f,sizeof(f));
    	f[0]=0;
    	solve(0,n+1);
    	printf("%d
    ",f[n+1]);
    	return 0;
    }
    

      

    H

    按极角排序,然后用一条线扫过去。

    然后狂WA第5个点,去膜了下Claris的代码,发现有些细节写错了。。。

    当一个点左右两个点极角比它小的时候,那么答案会加一,否则会减一。

    如果连续一条直线上极角都相等,只拿端点算贡献。

    然后这些点分为两类,假设现在答案要加一。

    如果这个点在右下(意会一下),那么只有扫描线扫过这个点时答案才会加一。

    如果在左上,那么扫到这个点时答案已经加了一。

    讨论一下,具体看代码。

    #include<bits/stdc++.h>
    #define N 100005
    #define ll long long
    using namespace std;
    int n;
    struct node
    {
    	int x,y;
    	node(){;}
    	node(int _x,int _y)
    	{
    		x=_x;y=_y;
    	}
    	friend node operator - (const node &aa,const node &bb)
    	{
    		return node(aa.x-bb.x,aa.y-bb.y);
    	}
    }a[N];
    int p[N];
    ll cj(const node &aa,const node &bb)
    {
    	return 1LL*aa.x*bb.y-1LL*aa.y*bb.x;
    }
    bool in[N],ok[N];
    bool cmp(int x,int y)
    {
    	return cj(a[x],a[y])>0;
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)scanf("%d%d",&a[i].x,&a[i].y);
    	a[0]=a[n];a[n+1]=a[1];
    	for(int i=1;i<=n;i++)p[i]=i;
    	sort(p+1,p+n+1,cmp);
    	int sum=0,ans=0;
    	for(int i=1;i<=n;)
    	{
    		node as=a[p[i]];
    		int tmp=0;
    		for(;i<=n&&cj(as,a[p[i]])==0;i++)
    		{
    			if(cj(a[p[i]+1],a[p[i]])<0&&cj(a[p[i]-1],a[p[i]])<=0)
    			{
    				if(cj(a[p[i]-1]-a[p[i]],a[p[i]+1]-a[p[i]])>0)sum++;
    				else tmp++;
    			}
    			if(cj(a[p[i]+1],a[p[i]])>0&&cj(a[p[i]-1],a[p[i]])>=0)
    			{
    				if(cj(a[p[i]-1]-a[p[i]],a[p[i]+1]-a[p[i]])<0)sum--;
    				else tmp--;
    			}
    		}
    		ans=max(ans,sum);
    		sum+=tmp;
    		ans=max(ans,sum);
    	}
    	printf("%d
    ",ans+1);
    	
    	return 0;
    }
    

      

    I

    傻逼题,一个子树要么给上边提供一个两个叶子的小子树,要么是一个叶子或零个,其他的只能直接配对,画画图大力分类讨论+贪心。

    #include<bits/stdc++.h>
    #define N 100005
    using namespace std;
    int n;
    int head[N],ver[N*2],nxt[N*2],tot;
    void add(int a,int b)
    {
    	tot++;nxt[tot]=head[a];head[a]=tot;ver[tot]=b;return ;
    }
    int du[N],root;
    int ans;
    int dfs(int x,int f)
    {
    	int t1=0,t2=0;
    	int cnt=0;
    	for(int i=head[x];i;i=nxt[i])
    	{
    		if(ver[i]==f)continue;
    		cnt++;
    		int tmp=dfs(ver[i],x);
    		if(tmp==1)t1++;
    		else if(tmp==2)t2++;
    	}
    	if(!cnt)return 1;
    	while(t2>=2)t2-=2,ans++;
    	while(t1>=3)t1-=2,ans++;
    	if(!t1)
    	{
    		if(t2==1)return 2;
    		return 0;
    	}
    	if(t1==1)
    	{
    		if(t2==1)return 2;
    		return 1;
    	}
    	else
    	{
    		if(!t2)return 2;
    		else if(t2==1){ans++;return 1;}
    		else {ans++;return 2;}
    	}
    }
    int main()
    {
    	scanf("%d",&n);
    	if(n==2)
    	{
    		puts("1");
    		return 0;
    	}
    	int t1,t2;
    	for(int i=1;i<n;i++)
    	{
    		scanf("%d%d",&t1,&t2);
    		add(t1,t2);add(t2,t1);
    		du[t1]++;du[t2]++;	
    	}
    	for(int i=1;i<=n;i++)if(du[i]>1)root=i;
    	int tmp=dfs(root,-1);
    	if(tmp==2)ans++;
    	printf("%d
    ",ans);
    	return 0;
    }
    

      

    J

    先假设一共有无数个0。

    枚举右端点,枚举左端点,然后把中间的1去掉,剩下的操作往里边塞0。

    列下式子发现左端点单调,可以用单调队列优化。

    最后把ans和0的个数取个min。

    #include<bits/stdc++.h>
    #define N 1000005
    using namespace std;
    char s[N];
    int n,mx,sum[N];
    int q[N];
    void solve(int x)
    {
    	int ha=1,ta=1;
    	q[1]=0;int ans=0;
    	for(int i=1;i<=n;i++)
    	{
    		while(ta>=ha&&sum[i]-sum[q[ha]]>x)ha++;
    		if(ta>=ha)
    		{
    			ans=max(ans,i-2*sum[i]+x+2*sum[q[ha]]-q[ha]);
    		}
    		while(ta>=ha&&2*sum[q[ta]]-q[ta]<=2*sum[i]-i)ta--;
    		q[++ta]=i;
    	}
    	printf("%d
    ",min(ans,mx));
    	return ;
    }
    int main()
    {
    	scanf("%s",s+1);
    	n=strlen(s+1);
    	for(int i=1;i<=n;i++)
    	{
    		sum[i]=sum[i-1];
    		if(s[i]=='1')sum[i]++;
    	}
    	for(int i=1;i<=n;i++)if(s[i]=='0')mx++;
    	int q;scanf("%d",&q);
    	for(int i=1;i<=q;i++)
    	{
    		int cnt;scanf("%d",&cnt);
    		solve(cnt);
    	}
    	return 0;
    }
    

      

     

  • 相关阅读:
    Text Link Ads 注册[赚钱一]
    Linux文件系统中的链接
    C++虚函数和纯虚函数(1)
    Android init reading tips
    Android上GDB的使用
    What is prelink?
    Linux fork哪些被继承,哪些不被继承
    为什么x86 Linux程序起始地址是从0x08048000开始的?
    Android应用开发的插件化 模块化
    C++拷贝构造函数(深拷贝、浅拷贝)
  • 原文地址:https://www.cnblogs.com/ezyzy/p/7028982.html
Copyright © 2011-2022 走看看