zoukankan      html  css  js  c++  java
  • 6546. 【GDOI2020模拟4.8】USACO 2020 Open Contest, Platinum(circus)

    题目描述

    Farmer John 马戏团的 N 头奶牛( 1 ≤ N ≤ 10^5 )正在准备她们接下来的演出。演出在一棵结点编号为 1 … N 的树上进行。演出的“起始状态”可以定义为一个整数 1 ≤ K ≤ N 以及奶牛 1 … K 在树上的结点分布,使得没有两头奶牛位于相同的结点。 在一场演出中,奶牛们可以进行任意次数的“移动”。在一次移动中,一头奶牛从她的当前所在结点移动到一个未被占据的相邻结点。称两个起始状态是等价的,如果一个状态可以通过一系列移动到达另一个。
    对于每一个 1 ≤ K ≤ N ,帮助奶牛们求出起始状态的等价类数量:即可选出的起始状态的最大数量,使得两两不等价。由于这些数字可能很大,输出模 10^9 + 7 的余数。

    N ≤ 10^5

    题解

    很神奇的题目

    猜结论,k只奶牛的位置不影响结果,最大数量只与奶牛间是否能交换有关,若干奶牛可以交换则只算一次

    证明不会

    所以可以假设k只奶牛都是当前深度最大的,只需要维护是否能交换即可,视作边

    选一个度数>=3的点做根,如果没有就是链

    关系有根与根之间的,父子间的以及兄弟(父亲不是根节点)间的,其余的关系可通过这三种得到

    设编号从下往上从小到大,显然k=n-1和n时答案为k!

    当k=n-2时,除编号为n-1的儿子以外,其余的根均相连

    k<=n-3时所有根相连

    当k<=n-2时兄弟相连

    对于每个点x设dis[x]表示x向上到某个度数>=3的点的距离,因为根节点的度数>=3所以必然存在

    所以x与儿子相连的条件是dis[x]+2<=n-k,即把向上的一段移开并多出两个来交换

    只可能向上,因为如果要在一个非根节点处向另一棵子树走的话,父亲边+走上来的边+走下去的边已经有至少三条了

    对于每种情况在考虑一下两端的出现时间,以及根的话要考虑父亲的出现时间,可以建一个0点来连

    最后变成若干条边,每条边有出现时间,求每个时间的连通情况,线段树分治+可撤销并查集即可

    code

    #include <bits/stdc++.h>
    #define fo(a,b,c) for (a=b; a<=c; a++)
    #define fd(a,b,c) for (a=b; a>=c; a--)
    #define min(a,b) (a<b?a:b)
    #define mod 1000000007
    #define Mod 1000000005
    #define ll long long
    #define file
    using namespace std;
    
    struct type{int x,y,X,Y,s;} b[300001],S;
    int a[200001][2],ls[100001],D[100001],d[100001],num[100001],dis[100001],size[100001];
    int fa[100001],dp[100001],n,i,j,k,l,root,len,h,t,tot;
    ll jc[100001],Jc[100001],w[100001],ans;
    vector<int> tr[400001];
    bool bz[100001];
    
    ll qpower(ll a,int b) {ll ans=1;while (b) {if (b&1) ans=ans*a%mod;a=a*a%mod;b>>=1;} return ans;}
    void New(int x,int y) {++len;a[len][0]=y;a[len][1]=ls[x];ls[x]=len;}
    
    void bfs()
    {
    	int i,j,k,l;
    	
    	h=0;t=1;d[1]=root;
    	while (h<t)
    	{
    		bz[d[++h]]=1;num[d[h]]=n-h+1;
    		for (i=ls[d[h]]; i; i=a[i][1])
    		if (!bz[a[i][0]])
    		{
    			bz[a[i][0]]=1;
    			d[++t]=a[i][0];
    		}
    	}
    }
    
    void change(int t,int l,int r,int x,int y,int s)
    {
    	int mid=(l+r)/2;
    	if (x<=l && r<=y) {tr[t].push_back(s);return;}
    	
    	if (x<=mid) change(t*2,l,mid,x,y,s);
    	if (mid<y) change(t*2+1,mid+1,r,x,y,s);
    }
    
    void add(int x,int y,type s) {if (x<=y) b[++tot]={s.x,s.y,0},change(1,1,n,x,y,tot);}
    
    void dfs(int Fa,int t,int Dis)
    {
    	int i,Ls=0;
    	dis[t]=Dis;
    	
    	if (dis[Fa]) add(num[Fa],n-dis[Fa]-2,{t,Fa,0});
    	if (num[Fa]==n-1) add(num[t],min(num[Fa]-1,n-3),{0,t,0}); else add(num[t],min(num[Fa]-1,n-2),{0,t,0});
    	
    	for (i=ls[t]; i; i=a[i][1])
    	if (a[i][0]!=Fa)
    	{
    		dfs(t,a[i][0],(D[t]<=2)?Dis+1:1);
    		if (Fa)
    		{
    			if (Ls) add(num[Ls],n-2,{Ls,a[i][0],0});
    			Ls=a[i][0];
    		}
    	}
    }
    
    int gf(int t) {if (fa[t]==t) return t;return gf(fa[t]);}
    void swap(int &x,int &y) {int z=x;x=y;y=z;}
    void link(int id)
    {
    	int x=b[id].x,y=b[id].y,X=gf(x),Y=gf(y);
    	if (X==Y) {b[id].X=-1; return;}
    	
    	if (dp[X]>dp[Y]) swap(X,Y);
    	fa[X]=Y;
    	if (dp[X]<dp[Y]) b[id].s=0; else b[id].s=1,++dp[Y];
    	
    	ans=ans*jc[size[X]]%mod*jc[size[Y]]%mod*Jc[size[X]+size[Y]]%mod;
    	size[Y]+=size[X];
    	b[id].X=X,b[id].Y=Y;
    }
    void cut(int id)
    {
    	int X=b[id].X,Y=b[id].Y;
    	if (X==-1) return;
    	
    	fa[X]=X;dp[Y]-=b[id].s;
    	ans=ans*jc[size[Y]]%mod*Jc[size[X]]%mod*Jc[size[Y]-size[X]]%mod;
    	size[Y]-=size[X];
    }
    
    void work(int t,int l,int r)
    {
    	int N=tr[t].size(),i,mid=(l+r)/2;
    	
    	if (l==r && l==3)
    	n=n;
    	
    	fo(i,0,N-1)
    	link(tr[t][i]);
    	if (l==r)
    	{
    		printf("%lld
    ",ans*jc[l]%mod);
    		fd(i,N-1,0) cut(tr[t][i]);
    		return;
    	}
    	
    	work(t*2,l,mid);
    	work(t*2+1,mid+1,r);
    	
    	fd(i,N-1,0) cut(tr[t][i]);
    }
    
    int main()
    {
    	freopen("circus.in","r",stdin);
    	#ifdef file
    	freopen("circus.out","w",stdout);
    	#endif
    	
    	scanf("%d",&n);
    	jc[0]=jc[1]=Jc[0]=Jc[1]=w[1]=1;
    	fo(i,2,n) w[i]=mod-w[mod%i]*(mod/i)%mod,jc[i]=jc[i-1]*i%mod,Jc[i]=Jc[i-1]*w[i]%mod,scanf("%d%d",&j,&k),New(j,k),New(k,j),++D[j],++D[k];
    	fo(i,1,n) fa[i]=i,size[i]=dp[i]=1;fa[0]=0;dp[0]=1;
    	fo(i,1,n) if (D[i]>2) {root=i;break;}
    	
    	if (!root) {fo(i,1,n) printf("%lld
    ",jc[i]);return 0;}
    	
    	bfs();
    	dfs(0,root,0);
    	ans=1;
    	work(1,1,n);
    	
    	fclose(stdin);
    	fclose(stdout);
    	
    	return 0;
    }
    
  • 相关阅读:
    RTSP协议视频智能分析平台EasyNVR新增H265播放器EasyPlayer和EasyWasmPlayer的区别介绍
    RTSP拉流协议视频平台EasyNVR硬件设备意外断电重启无法进入initramfs界面配置如何解决?
    RTSP拉流协议视频平台EasyNVR多点认证造成潜在威胁?EasyNVR多点认证机制优化
    【BUG修复】网络流媒体协议RTSP拉流平台EasyNVR增加鉴权抵御外部攻击优化
    RTSP视频平台EasyNVR作为下级平台通过GB28181级联传输视频流时的多播Multicast协议介绍
    【硬件设备】RTSP协议安防视频平台EasyNVR视频边缘计算网关设备如何关闭图形化网卡配置功能?
    【硬件设备】RTSP协议安防视频平台EasyNVR视频边缘计算网关设备修改/etc/network/interfaces后配置界面消失如何修复?
    RTSP协议视频智能分析/内容识别平台EasyNVR安防视频云服务关于POST接口C#调用的例子介绍说明
    RTSP协议视频智能分析/智能识别系统EasyNVR新增演示模式下用户登录有效期限时5分钟说明
    RTSP协议视频智能分析/智能识别服务平台EasyNVR新增自定义登录失败锁定用户功能
  • 原文地址:https://www.cnblogs.com/gmh77/p/12685446.html
Copyright © 2011-2022 走看看