P6773 NOI2020 命运
P6773 NOI2020 命运
数据结构上做 dp,少见但好用。
思路
首先我们用 dp 来解决这个问题。由于与祖先有关,我们不妨把一个节点的子问题限定在子树内,考虑所有从子树内连向子树外的集合
设
第一项为选择原树边
考虑设计状态
原式可以写为:
显然初始值
我们使用线段树来维护区间
我们参考类似 cdq 分治维护 dp 的方法,从左到右转移时同时记录下当前的
CODE
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mod 998244353
const int maxn=5e5+5;
struct Edge
{
int tot;
int head[maxn];
struct edgenode{int to,nxt;}edge[maxn*2];
inline void add(int x,int y)
{
tot++;
edge[tot].to=y;
edge[tot].nxt=head[x];
head[x]=tot;
}
}T;
int n,m;
int dep[maxn],rt[maxn];
vector<int>E[maxn];
ll sumu,sumv;
namespace linetree
{
#define lch(p) tr[p].lch
#define rch(p) tr[p].rch
int tot;
struct treenode{int lch,rch;ll sum,tag;}tr[maxn*20];
inline int newnode(){tr[++tot].tag=1;return tot;}
inline void pushup(int p){tr[p].sum=(tr[lch(p)].sum+tr[rch(p)].sum)%mod;}
inline void pushdown(int p)
{
if(tr[p].tag!=1)
{
if(lch(p)) tr[lch(p)].sum=(tr[lch(p)].sum*tr[p].tag)%mod;
if(lch(p)) tr[lch(p)].tag=(tr[lch(p)].tag*tr[p].tag)%mod;
if(rch(p)) tr[rch(p)].sum=(tr[rch(p)].sum*tr[p].tag)%mod;
if(rch(p)) tr[rch(p)].tag=(tr[rch(p)].tag*tr[p].tag)%mod;
tr[p].tag=1;
}
}
inline void change(int &p,int l,int r,int pos,ll val)
{
if(!p) p=newnode();
if(l==r){tr[p].sum=val,tr[p].tag=1;return ;}
pushdown(p);
int mid=(l+r)>>1;
if(pos<=mid) change(lch(p),l,mid,pos,val);
else change(rch(p),mid+1,r,pos,val);
pushup(p);
}
inline ll qry(int p,int l,int r,int lx,int rx)
{
if(r<lx||l>rx||!p) return 0;
if(lx<=l&&r<=rx) return tr[p].sum;
pushdown(p);
int mid=(l+r)>>1;
return (qry(lch(p),l,mid,lx,rx)+qry(rch(p),mid+1,r,lx,rx))%mod;
}
inline void merge(int &p,int tp,int l,int r)
{
if(!p&&!tp) return ;
if(!p)
{
sumv=(sumv+tr[tp].sum)%mod;
tr[tp].tag=(tr[tp].tag*sumu)%mod;
tr[tp].sum=(tr[tp].sum*sumu)%mod;
p=tp;
return ;
}
else if(!tp)
{
sumu=(sumu+tr[p].sum)%mod;
tr[p].tag=(tr[p].tag*sumv)%mod;
tr[p].sum=(tr[p].sum*sumv)%mod;
return ;
}
else if(l==r)
{
ll tmp=tr[p].sum;
sumv=(sumv+tr[tp].sum)%mod;
tr[p].sum=(tr[p].sum*sumv%mod+tr[tp].sum*sumu%mod)%mod;
sumu=(sumu+tmp)%mod;
return ;
}
pushdown(tp);pushdown(p);
int mid=(l+r)>>1;
merge(lch(p),lch(tp),l,mid);merge(rch(p),rch(tp),mid+1,r);
pushup(p);
}
}
inline void dfs(int u,int f)
{
dep[u]=dep[f]+1;int mxd=0;
for(auto v:E[u]) mxd=max(dep[v],mxd);
linetree::change(rt[u],0,n,mxd,1);
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(v==f) continue;
dfs(v,u);
sumv=linetree::qry(rt[v],0,n,0,dep[u]);sumu=0;
linetree::merge(rt[u],rt[v],0,n);
}
}
int main()
{
freopen("destiny.in","r",stdin);
freopen("destiny.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
T.add(u,v),T.add(v,u);
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
E[v].emplace_back(u);
}
dfs(1,0);
printf("%lld",linetree::qry(rt[1],0,n,0,0));
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!