zoukankan      html  css  js  c++  java
  • JZOJ6392. 【NOIP2019模拟2019.10.26】僵尸

    Description

    传送门
    在这里插入图片描述
    T<=5,1<=n,m<=2000,1<=li,ri,hi<=1e9T<=5,1<=n,m<=2000,1<=li,ri,hi<=1e9

    Solution

    • 首先很容易想到的是求所有节点都被占领的概率,也就是方案数(最后再除以总数)。
    • 考场上的时候想到了状态f[x][i]f[x][i]表示x的子树全部被占领,从x的子树上到x节点上的最大能力的僵尸是ii
    • 但是难处理的是僵尸会从一个子树上去再下来到某个节点,单单是考虑x的子树中的僵尸是不行的,要考虑到所有的僵尸。
    • 所以就有了一种很巧妙的方法,将f[x][i]f[x][i]变为在整棵树上的僵尸ii到了x。有可能这个i不在x的子树中,但是它可以从父亲走过来,所以我们就假设它到了x,并且预先把它的贡献算出来(这是我觉得这题设的状态最巧妙并且最难懂的地方)
    • 既然已经理解了状态的话,方程就不难推了。
    • 考虑x是当前点,y是它的儿子。假设y->x的边被k这个僵尸通过的方案为wkw_k,通不过的方案为vkv_k
    1. f[x][k]+=f[x][k]f[y][k]wkf'[x][k]+=f[x][k]*f[y][k]*w_k,表示x或y如果有k的话,那这个k也通过(x,y)走到另一边,不用管是从下往上还是从上往下,为了让k走过去,就要有wkw_k
    2. f[x][k]+=f[x][k]t>kf[y][t]vtf'[x][k]+=f[x][k]*sum_{t>k}{f[y][t]}*v_t,表示当t比k要强的时候,t不能跳到k。注意t一定在y的子树内,k一定不在y的子树内。
    3. f[x][k]+=f[x][k]vkt<kf[y][t]f'[x][k]+=f[x][k]*v_k*sum_{t<k}{f[y][t]},即t比k弱时,k不能跳到t。t,k范围同上。
    • f[x]的初值:假设x点起始的僵尸最强的是p,那么f[x][i]=(i>=p)f[x][i]=(i>=p),因为不管僵尸怎么走,起始的时候的最大僵尸就一定不小于p。
    • 用前缀(后缀)和优化。
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define maxn 2005
    #define ll long long 
    #define mo 998244353
    using namespace std;
    
    int T,n,m,i,j,k,x,y,u,v,tot;
    int em,e[maxn*2],nx[maxn*2],ls[maxn],L[maxn*2],R[maxn*2];
    int hv[maxn][maxn];
    struct zom{int s,h;} z[maxn];
    int cmp(zom a,zom b){return a.h<b.h;}
    ll sum,ans,f[maxn][maxn],g[maxn],s[maxn];
    
    ll ksm(ll x,ll y){
    	ll s=1;
    	for(;y;y/=2,x=x*x%mo) if (y&1) 
    		s=s*x%mo;
    	return s;
    }
    
    ll Get(int i,int k,int t){
    	if (t==1) return min(max(0,z[k].h-L[i]),R[i]-L[i]+1);
    	return min(max(0,R[i]-z[k].h+1),R[i]-L[i]+1);
    }
    
    void dg(int x,int p){
    	int i,j,y; ll s;
    	for(i=ls[x];i;i=nx[i]) if (e[i]!=p)
    		dg(e[i],x);
    	int tp=1;
    	for(i=m;i>=1;i--) {
    		f[x][i]=tp;
    		if (hv[x][i]) tp=0;
    	}
    	for(i=ls[x];i;i=nx[i]) if (e[i]!=p){
    		y=e[i];
    		for(j=1;j<=m;j++) hv[x][j]|=hv[y][j];
    		memcpy(g,f[x],sizeof(g));
    		memset(f[x],0,sizeof(f[x]));
    		for(j=1;j<=m;j++) f[x][j]+=g[j]*f[y][j]%mo*Get(i,j,1)%mo;
    		for(s=0,j=m;j>=1;j--){
    			if (!hv[y][j]) f[x][j]+=s*g[j]%mo;
    			if (hv[y][j]) (s+=f[y][j]*Get(i,j,0)%mo)%=mo;
    		}
    		for(s=0,j=1;j<=m;j++) {
    			if (!hv[y][j]) f[x][j]+=s*g[j]%mo*Get(i,j,0)%mo;
    			if (hv[y][j]) (s+=f[y][j])%=mo;
    		}
    	}
    	for(i=1;i<=m;i++) f[x][i]%=mo;
    }
    
    int main(){
    	scanf("%d",&T);
    	while (T--){
    		scanf("%d%d",&n,&m);
    		em=0,memset(ls,0,sizeof(ls));
    		sum=1;
    		for(i=1;i<n;i++){
    			scanf("%d%d%d%d",&x,&y,&u,&v);
    			em++; e[em]=y; nx[em]=ls[x]; ls[x]=em; L[em]=u,R[em]=v;
    			em++; e[em]=x; nx[em]=ls[y]; ls[y]=em; L[em]=u,R[em]=v;
    			sum=sum*(v-u+1)%mo;
    		}
    		for(i=1;i<=m;i++) scanf("%d%d",&z[i].s,&z[i].h);
    		sort(z+1,z+1+m,cmp);
    		memset(hv,0,sizeof(hv));
    		for(i=1;i<=m;i++) hv[z[i].s][i]=1;
    		dg(1,0);
    		for(ans=0,i=1;i<=m;i++) ans+=f[1][i];
    		printf("%lld
    ",((sum-ans%mo)%mo*ksm(sum,mo-2)%mo+mo)%mo);
    	}
    }
    
    
  • 相关阅读:
    Java 蓝桥杯 算法训练 貌似化学
    Java 蓝桥杯 算法训练 貌似化学
    Java 蓝桥杯 算法训练 字符串的展开 (JAVA语言实现)
    Java 蓝桥杯 算法训练 字符串的展开 (JAVA语言实现)
    Java 蓝桥杯 算法训练 字符串的展开 (JAVA语言实现)
    Java 蓝桥杯 算法训练 字符串的展开 (JAVA语言实现)
    Java 蓝桥杯 算法训练 字符串的展开 (JAVA语言实现)
    JAVA-蓝桥杯-算法训练-字符串变换
    Ceph:一个开源的 Linux PB 级分布式文件系统
    shell 脚本监控程序是否正在执行, 如果没有执行, 则自动启动该进程
  • 原文地址:https://www.cnblogs.com/DeepThinking/p/13090935.html
Copyright © 2011-2022 走看看