• CometOJ #6 D 另一道树题


    题目描述

    短发pinga野郞有一棵 (N) 个节点的有根树,其中 (1) 号节点为根。

    现在她想在这棵树上进行一种奇妙的游戏。游戏开始时,她会在其中一些节点(至少两个)上摆放一个棋子。在游戏的每个回合中,她会将所有棋子移动到父亲节点上(如棋子位于根节点则不移动)。移动完所有棋子后,如果有两个棋子位于同一个节点,游戏结束,否则进行下一回合直到游戏结束。

    定义游戏的有趣程度为游戏结束前进行的总回合数。现在,pinga野郞想知道,对于所有可能的 (2^N-N-1) 种初始摆放方案,游戏的有趣程度之和是多少。请你将答案对 (998244353) 取模后输出。

    (N le 2e5)

    首先转换成(> k)的方案数

    考虑除了在根节点相遇的,所有相遇的节点深度相同。

    对于一个(k),显然一个点的(k)级祖先对这些点的限制最紧,即我们可以对于每个节点统计距离它深度为(j)的儿子,然然后这些节点中最多选择一个。

    可以用长链剖分做距离它深度为(j)的儿子个数,但是发现长链处的贡献没法暴力for。

    可以发现每一个dp值(这里定义为一个位置一段时间的dp值)会贡献到的(k)是一段区间,下界可以直接记下(更新的时间),上界既是它被弹出的时间。

    根节点特判。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int mod=998244353;
    inline int add(int a,int b){a+=b;return a>=mod?a-mod:a;}
    inline int sub(int a,int b){a-=b;return a<0?a+mod:a;}
    inline int mul(int a,int b){return 1ll*a*b%mod;}
    inline int qpow(int a,int b){int ret=1;for(;b;b>>=1,a=mul(a,a))if(b&1)ret=mul(ret,a);return ret;}
    inline int qinv(int x){return qpow(x,mod-2);}
    /* math */
    const int N = 4e5+5;
    int n,ans[N];
    int hed[N],to[N],nxt[N],cnt,len[N],ls[N];
    inline void adde(int u,int v){
    	++cnt;to[cnt]=v,nxt[cnt]=hed[u];hed[u]=cnt;
    }
    int multag[N];
    inline void push(int l,int r,int sum){
    	++sum;
    	multag[l]=mul(multag[l],sum),multag[r+1]=mul(multag[r+1],qinv(sum));
    }
    
    inline void predfs(int x,int pre){
    	ls[x] = 0;
    	for(int i=hed[x];i;i=nxt[i]){
    		int v=to[i];if(v==pre)continue;
    		predfs(v,x);
    		if(len[ls[x]] < len[v]+1) ls[x] = v;
    	}
    	len[x] = len[ls[x]]+1;
    }
    struct data{
    	int siz,intime;
    	data(int a=0,int b=0):siz(a),intime(b){}
    };
    
    void dfs(int x,int pre,data *f){
    	if(ls[x]) dfs(ls[x],x,f+1);
    	f[0].siz = 1;f[0].intime = 0;
    	vector<data> cur;
    	for(int e=hed[x];e;e=nxt[e]){
    		int v=to[e];if(v==pre||v==ls[x])continue;
    		data *g = new data[len[v]];
    		for(int i=0;i<len[v];i++) g[i] = data(0,0);
    		dfs(v,x,g);
    		while((int)cur.size()<len[v])cur.push_back(data(0,0));
    		for(int i=0;i<len[v];i++) {
    			cur[i].siz += g[i].siz;
    			push(g[i].intime,i,g[i].siz);
    		}
    	}
    	for(size_t i=1;i<=cur.size();i++){
    		push(f[i].intime,i-1,f[i].siz);
    		f[i].intime = i;
    		f[i].siz += cur[i-1].siz;
    	}
    }
    
    int main()
    {
    	cin >> n;
    	for(int i=0;i<=n+1;i++)multag[i]=1;
    	for(int i=2;i<=n;i++){;
    	int u=i,v;scanf("%d",&v);
    		adde(u,v),adde(v,u);
    	}
    	predfs(1,0);
    	data *f = new data[len[1]];
    	for(int i=0;i<len[1];i++)f[i]=data(0,0);
    	dfs(1,0,f);
    	for(int i=1;i<len[1];i++)
    		push(f[i].intime,i-1,f[i].siz);
    	int cursz=0;
    	for(int i=0;i<len[1];i++){
    		cursz += f[i].siz;
    		push(i,i,cursz);
    	}
    	for(int i=len[1];i<=n+2;i++)
    		push(i,i,cursz);	
    	ans[0]=multag[0];
    	for(int i=1;i<=n+1;i++)
    		ans[i] = mul(ans[i-1],multag[i]);
    	int ret=0;
    	for(int i=0;i<=n;i++)
    		ret=add(ret,mul(i+1, sub(ans[i],ans[i+1])));
    	cout << ret << endl;
    }
    
  • 相关阅读:
    西瓜书的读书笔记
    Tensorflow-gpu在windows10上的安装(anaconda)
    php 设置报错等级
    PHP 类型比较表
    PHP中的定界符格式
    php进制转换函数
    php魔法常量
    php中双$$与多$$
    php引用传值
    CSS3过渡效果实现菜单划出效果
  • 原文地址:https://www.cnblogs.com/weiyanpeng/p/11105379.html
Copyright © 2020-2023  润新知