6546. GDOI2020模拟4.8USACO 2020 Open Contest, Platinum(circus)
Posted gmh77
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了6546. GDOI2020模拟4.8USACO 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;
}
以上是关于6546. GDOI2020模拟4.8USACO 2020 Open Contest, Platinum(circus)的主要内容,如果未能解决你的问题,请参考以下文章