zoukankan      html  css  js  c++  java
  • hard(2018.10.18)

    题意:给你一棵(n)个节点的树,(q)个询问,每次询问读入(u,v,k,op),需要满足树上有(k)对点的简单路径交都等于(u,v)之间的简单路径,(op=1)表示(k)对点中每个点只能存在于一个点对中,否则每个点可以存在于多个点对中,问那k对点有多少种选法,答案对(998244353)取模。
    数据范围:对于(100%)的数据,保证 (1≤n≤10^5,1≤u,v≤n,u e v,1≤k≤min(n,500),op∈{0,1}),保证每个节点的度数不超过(500)

    我们抓住“两两路径之交是((u,v))”这条性质。 可以发现(u,v)是独立的。我们等价于要求:在(u)的子树中选(k)个点使它们两两(lca)(u)的方案数,对(v)也求同样的东西,再把两者相乘。如果(u,v)存在祖孙关系,不妨设(u)(v)的祖先,那么(u)的子树就要改为以(v)的方向作为根方向前提下的子树。 显然为了使两两(lca)(u),在(u)的每一个儿子中就至多只能选一个点。然后这题就差不多了。

    (g[x][i])为在(x)的子树里选(i)个点的方案数,(ans)为最后的答案,(u,v)为读入的(u,v),钦定(u)为深度小的那个点,(tmp)(u-v)路径上最靠近(u)的那个点
    那么有:

    [if(lca(u,v)==u)ans=tmp[k]*g[v][k] ]

    [else ans=g[u][k]*g[v][k] ]

    注意:(k)个点对是不等价的,比如说我们可以选((i,j))为第一个点对和选((i,j))为第二个点对是两种方案。
    代码:

    #include<cstdio>
    #include<algorithm>
    int n,q,cnt,fac[501],inv[501],facinv[501],pre[200001],nxt[200001],h[100001],f[100001][20],size[100001],dep[100001],mod=998244353;
    struct oo{
    	int d[601],du;oo(){d[du=0]=1;}
    	void add(int x){du++;for(int i=du;i;i--)d[i]=(d[i]+1ll*d[i-1]*x)%mod;}
    	void del(int x){for(int i=1;i<=du;i++)d[i]=((d[i]-1ll*d[i-1]*x)%mod+mod)%mod;du--;}
    	int cal(int x,int op){int ans=0;for(int i=op?x-1:0;i<=x;i++)ans=(ans+1ll*d[i]*facinv[x-i])%mod;return (1ll*ans*fac[x])%mod;}
    }g[100001];
    void add(int x,int y){
    	pre[++cnt]=y;nxt[cnt]=h[x];h[x]=cnt;
    	pre[++cnt]=x;nxt[cnt]=h[y];h[y]=cnt;}
    void dfs(int x){size[x]=1;
    	for(int i=1;i<20;i++){if(dep[x]<(1<<i))break;f[x][i]=f[f[x][i-1]][i-1];}
    	for(int i=h[x];i;i=nxt[i])if(pre[i]!=f[x][0]){dep[pre[i]]=dep[x]+1,f[pre[i]][0]=x,dfs(pre[i]),size[x]+=size[pre[i]];g[x].add(size[pre[i]]);}}
    int lca(int x,int y){
    	if(dep[x]>dep[y])std::swap(x,y);int poor=dep[y]-dep[x];
    	for(int i=19;i>=0;i--)if(poor&(1<<i))y=f[y][i];
    	for(int i=19;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    	if(x==y)return x;return f[x][0];}
    int get(int x,int y){int poor=dep[x]-dep[y]-1;for(int i=19;i>=0;i--)if(poor&(1<<i))x=f[x][i];return x;}
    int main(){
    	scanf("%d%d",&n,&q);inv[1]=fac[0]=facinv[0]=1;for(int i=2;i<=500;i++)inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
    	for(int i=1;i<=500;i++)fac[i]=1ll*fac[i-1]*i%mod,facinv[i]=1ll*facinv[i-1]*inv[i]%mod;
    	for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),add(x,y);dfs(1);
    	for(int i=1,u,v,k,op;i<=q;i++){
    		scanf("%d%d%d%d",&u,&v,&k,&op);if(dep[u]>dep[v])std::swap(u,v);
    		if(lca(u,v)==u){
    			int now=get(v,u);oo s=g[u];s.del(size[now]),s.add(n-size[u]);
    			printf("%d
    ",(1ll*s.cal(k,op)*g[v].cal(k,op))%mod);}
    		else printf("%d
    ",(1ll*g[u].cal(k,op)*g[v].cal(k,op))%mod);}}
    
  • 相关阅读:
    PICT安装及使用
    Linux 压缩和解压缩常用命令
    ICTCLAS 汉语词性标注集
    基于MPI的并行计算—矩阵向量乘
    Python 主成分分析PCA
    Python 利用pytesser模块识别图像文字
    华为HG255D路由器使用OH3C进行中大校园网认证
    Kubuntu 使用YaH3C进行中大校园网认证
    PHP 调用Python脚本
    Python 调用百度翻译API
  • 原文地址:https://www.cnblogs.com/lcxer/p/9815353.html
Copyright © 2011-2022 走看看