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);
    	}
    }
    
    
  • 相关阅读:
    spring配置详解
    表单重复提交解决办法
    Java 两个变量交换值
    spring @ExceptionHandler注解方式实现异常统一处理
    mybatis实战
    使用soapui调用webservice接口
    使用火狐的restclient发送http接口post及get请求
    很多网站301重定向
    邮件发布google blogger 博客
    php file取重复
  • 原文地址:https://www.cnblogs.com/DeepThinking/p/13090935.html
Copyright © 2011-2022 走看看