zoukankan      html  css  js  c++  java
  • UOJ#468. 【ZJOI2019】Minimax搜索 动态DP

    原文链接www.cnblogs.com/zhouzhendong/p/UOJ468.html

    前言

    毒瘤题

    题解

    首先,将问题稍加转化,将“等于k”转化为“小于等于k”减去“小于k”。

    然后,考虑在有一个变化量限制k时,所有的叶子会怎样变化。

    我们称原本根的权值对应的节点到根的路径为“主链”,那么,只要主链的任何一个节点的权值发生变化,那么根节点权值就发生变化。

    我们称一个主链上的节点的在主链上的儿子为“主儿子”。

    对于主链上的一个节点,假设他深度为奇数,那么他的所有子树中,除了主儿子所在子树以外的所有叶子节点都会加上k。否则,假设他深度为偶数,那么这些叶子节点会减去k。

    于是,接下来我们就可以得到一个 (O(n)) 的 DP 方法。即

    对于深度为奇数的主链的子树, (dp[x][0])(dp[x][1]) 分别表示在子树x的所有叶子节点集合操作时,有 (dp[x][1]) 个集合可以使得 x 的权值大于 1 号点原先的权值,有 (dp[x][0]) 个不能。

    对于深度为偶数的主链的子树, (dp[x][1])(dp[x][0]) 分别表示在子树x的所有叶子节点集合操作时,有 (dp[x][1]) 个集合可以使得 x 的权值小于 1 号点原先的权值,有 (dp[x][1]) 个不能。

    请读者自行列出 DP 转移。

    事实上,这种 DP 状态是可以简化的。由于对于一个 x,(dp[x][0]+dp[x][1]) 是固定的,所以我们只需要记一个。虽然这不影响得分。

    至此,我们得到了一个单次 DP (O(n)),总时间复杂度 (O((R-L)cdot n)) 的做法。

    我们发现,当k的值从小到大不断变大时,只会有 (O(n)) 个叶子的 DP 值发生变化,每次变化会影响它到根的路径。

    简单分析即可发现这里可以用动态DP来维护。

    于是我们就得到了一个 (O(nlog ^ 2 n)) 的做法。

    注意维护的时候可以会遇到乘0和除0的情况,注意特判。

    代码

    #pragma GCC optimize("Ofast","inline")
    #include <bits/stdc++.h>
    #define clr(x) memset(x,0,sizeof x)
    #define For(i,a,b) for (int i=(a);i<=(b);i++)
    #define Fod(i,b,a) for (int i=(b);i>=(a);i--)
    #define pb(x) push_back(x)
    #define mp(x,y) make_pair(x,y)
    #define fi first
    #define se second
    #define next Next
    #define outval(x) cerr<<#x" = "<<x<<endl
    #define outtag(x) cerr<<"-----------------"#x"-----------------
    "
    #define outarr(a,L,R) cerr<<#a"["<<L<<".."<<R<<"] = ";
                        For(_x,L,R) cerr<<a[_x]<<" ";cerr<<endl;
    using namespace std;
    typedef long long LL;
    typedef unsigned long long ULL;
    typedef pair <int,int> pii;
    LL read(){
        LL x=0,f=0;
        char ch=getchar();
        while (!isdigit(ch))
            f=ch=='-',ch=getchar();
        while (isdigit(ch))
            x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        return f?-x:x;
    }
    const int N=200005,mod=998244353;
    int Pow(int x,int y){
    	int ans=1;
    	for (;y;y>>=1,x=(LL)x*x%mod)
    		if (y&1)
    			ans=(LL)ans*x%mod;
    	return ans;
    }
    void Add(int &x,int y){
    	if ((x+=y)>=mod)
    		x-=mod;
    }
    void Del(int &x,int y){
    	if ((x-=y)<0)
    		x+=mod;
    }
    int Add(int x){
    	return x>=mod?x-mod:x;
    }
    int Del(int x){
    	return x<0?x+mod:x;
    }
    int n,qL,qR;
    vector <int> e[N];
    int depth[N],size[N],son[N],val[N];
    int fa[N],leaf[N],clf[N],pw2[N],clf2[N];
    int fun(int k,int a,int b){
    	return k&1?max(a,b):min(a,b);
    }
    void dfs(int x,int pre,int d){
    	depth[x]=d,size[x]=1,son[x]=0,fa[x]=pre;
    	if (e[x].size()==1&&d>1)
    		val[x]=x,leaf[x]=clf[x]=1;
    	else
    		val[x]=d&1?0:n;
    	for (auto y : e[x])
    		if (y!=pre){
    			dfs(y,x,d+1);
    			clf[x]+=clf[y];
    			size[x]+=size[y];
    			val[x]=fun(d,val[x],val[y]);
    			if (!son[x]||size[y]>size[son[x]])
    				son[x]=y;
    		}
    }
    int next[N],fad[N];
    void dfs2(int x,int d){
    	fad[x]=d;
    	for (auto y : e[x])
    		if (y!=fa[x]&&y!=next[x])
    			dfs2(y,d);
    }
    void GetNext(){
    	int x=val[1];
    	for (int y=fa[x];y;x=y,y=fa[x])
    		next[y]=x,dfs2(y,depth[y]);
    }
    int top[N],I[N],aI[N],Time=0;
    void GetTop(int x,int tp){
    	top[x]=tp,I[x]=++Time,aI[Time]=x;
    	if (son[x]&&son[x]!=next[x])
    		GetTop(son[x],tp);
    	for (auto y : e[x])
    		if (!I[y])
    			GetTop(y,y);
    }
    int ans[N];
    int dp[N];
    int delta;
    void DP(int x){
    	dp[x]=0;
    	if (x==val[1]){
    		dp[x]=1;
    		return;
    	}
    	if (leaf[x]){
    		if (fad[x]&1){
    			dp[x]+=(x+delta>val[1])==(depth[x]&1);
    			dp[x]+=(x>val[1])==(depth[x]&1);
    		}
    		else {
    			dp[x]+=(x-delta>=val[1])==(depth[x]&1);
    			dp[x]+=(x>=val[1])==(depth[x]&1);
    		}
    		return;
    	}
    	dp[x]=1;
    	for (auto y : e[x])
    		if (y!=fa[x]){
    			DP(y);
    			if (y!=next[x])
    				dp[x]=(LL)dp[x]*dp[y]%mod;
    		}
    	dp[x]=Del(pw2[clf2[x]]-dp[x]);
    }
    struct int0{
    	int v,c;
    	int0(){}
    	int0(int _v,int _c){
    		v=_v,c=_c;
    	}
    	int f(){
    		return c?0:v;
    	}
    	void operator *= (int x){
    		if (!x)
    			c++;
    		else
    			v=(LL)v*x%mod;
    	}
    	void operator /= (int x){
    		if (!x)
    			c--;
    		else
    			v=(LL)v*Pow(x,mod-2)%mod;
    	}
    }v2[N],now;
    int0 Get(){
    	int0 ans=int0(1,0);
    	for (int x=val[1];x;x=fa[x])
    		ans*=Del(pw2[clf2[x]]-dp[x]);
    	return ans;
    }
    struct fuck{
    	int a,b;
    	fuck(){}
    	fuck(int _a,int _b){
    		a=_a,b=_b;
    	}
    	friend fuck operator * (fuck x,fuck y){
    		return fuck((LL)x.a*y.a%mod,((LL)x.a*y.b+x.b)%mod);
    	}
    	int calc(int x){
    		return ((LL)a*x+b)%mod;
    	}
    }v[N];
    int mxd[N];
    namespace Seg{
    	fuck s[N<<2];
    	void Build(int rt,int L,int R){
    		if (L==R)
    			return (void)(s[rt]=v[aI[L]]);
    		int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
    		Build(ls,L,mid);
    		Build(rs,mid+1,R);
    		s[rt]=s[ls]*s[rs];
    	}
    	void update(int rt,int L,int R,int x,fuck v){
    		if (L==R)
    			return (void)(s[rt]=v);
    		int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
    		if (x<=mid)
    			update(ls,L,mid,x,v);
    		else
    			update(rs,mid+1,R,x,v);
    		s[rt]=s[ls]*s[rs];
    	}
    	fuck query(int rt,int L,int R,int xL,int xR){
    		if (R<xL||L>xR)
    			return fuck(1,0);
    		if (xL<=L&&R<=xR)
    			return s[rt];
    		int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
    		return query(ls,L,mid,xL,xR)*query(rs,mid+1,R,xL,xR);
    	}
    }
    void update(int x){
    	while (1){
    		Seg::update(1,1,n,I[x],v[x]);
    		x=top[x];
    		int f=fa[x];
    		if (val[x]!=val[1])
    			v2[f]/=dp[x];
    		else
    			now/=Del(pw2[clf2[x]]-dp[x]);
    		dp[x]=Seg::query(1,1,n,I[x],I[mxd[x]]).calc(1);
    		if (val[x]!=val[1])
    			v2[f]*=dp[x],v[f].a=v2[f].f(),x=f;
    		else {
    			now*=Del(pw2[clf2[x]]-dp[x]);
    			break;
    		}
    	}
    }
    vector <int> upds[N];
    int main(){
    	n=read(),qL=read(),qR=read();
    	pw2[0]=1;
    	For(i,1,n)
    		pw2[i]=Add(pw2[i-1]<<1);
    	For(i,1,n-1){
    		int x=read(),y=read();
    		e[x].pb(y),e[y].pb(x);
    	}
    	dfs(1,0,1);
    	GetNext();
    	GetTop(1,1);
    	For(i,1,n)
    		clf2[i]=clf[i];
    	for (int x=val[1];x;x=fa[x])
    		clf2[x]-=clf[next[x]];
    	delta=1,DP(1),now=Get(),ans[1]=Del(pw2[clf[1]]-now.f());
    	For(x,1,n){
    		if (leaf[x])
    			v[x]=fuck(0,dp[x]);
    		else {
    			v2[x]=int0(Del(-1),0);
    			for (auto y : e[x])
    				if (y!=fa[x]&&y!=next[x]&&y!=son[x])
    					v2[x]*=dp[y];
    			v[x]=fuck(v2[x].f(),pw2[clf2[x]]);
    		}
    	}
    	For(x,1,n)
    		if (top[x]==x)
    			mxd[x]=x;
    	For(x,1,n)
    		if (depth[x]>depth[mxd[top[x]]])
    			mxd[top[x]]=x;
    	Seg::Build(1,1,n);
    	For(i,1,n){
    		if (!leaf[i]||i==val[1])
    			continue;
    		if (fad[i]&1){
    			if (i<val[1])
    				upds[val[1]-i+1].pb(i);
    		}
    		else {
    			if (i>val[1])
    				upds[i-val[1]+1].pb(i);
    		}
    	}
    	For(i,2,n-1){
    		delta=i;
    		for (auto x : upds[i]){
    			int tmp=0;
    			if (fad[x]&1){
    				tmp+=(x+delta>val[1])==(depth[x]&1);
    				tmp+=(x>val[1])==(depth[x]&1);
    			}
    			else {
    				tmp+=(x-delta>=val[1])==(depth[x]&1);
    				tmp+=(x>=val[1])==(depth[x]&1);
    			}
    			v[x]=fuck(0,tmp);
    			update(x);
    		}
    		ans[i]=Del(pw2[clf[1]]-now.f());
    	}
    	ans[n]=Del(pw2[clf[1]]-1);
    	For(i,qL,qR)
    		printf("%d ",Del(ans[i]-ans[i-1]));
    	puts("");
    	return 0;
    }
    
  • 相关阅读:
    01开卡主流程
    eclipse开发工程右键菜单例子
    eclipse插件开发需要什么
    installshield安装程序自注册dll
    eclipse开发视图
    vbscript和cmd命令 如何获取当前文件的路径
    eclipse开发视图
    vbscript和cmd命令 如何获取当前文件的路径
    vbscript设置环境变量
    eclipse开发工程右键菜单例子
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/UOJ468.html
Copyright © 2011-2022 走看看