前言
真不错,保留字挂分真不戳。
T1 做过。
本来 T2 暴力有 (60)pts ,结果用了 c++14 的保留字move
直接 RE 了...
T3 暴力被卡 TLE 了...正解的分类讨论想出来了但是没想到用树状数组和 set 维护,而且没时间了。
总分 (100)pts.
题解
着重说一下这个 T2.
发现这种树形结构的题,转化成线段树区间维护我经常想不到...
首先普遍思路是要对于每一个动物,去打别人赢的概率是 (frac{1}{3}) ,被别人打赢的概率是 (frac{2}{3}) .
暴力的思路就是暴力地合并(链表,set ,并查集等均可),每次查询即为 (3^n imes) 赢的概率。
正解先把询问离线下来,按照操作的时间顺序从 (v->u) 建边(其实只需要链前倒序建边即可)并且 dfs 求出 dfs 序。
然后就可以把每段要操作的笼子转化成连续的区间。区间连续就能用线段树维护区间乘了。
线段树的叶子节点初始化为 (3^n) ,维护的时候只需要维护叶子节点即可,因为单点查只有叶节点有意义。
区间乘不太常见,注意一下lazy
数组的更新和清空。
(线段树区间修改别忘记套dfn
)
T2-zoo 代码
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f,N = 2e5+10,mod = 998244353;
#define ll long long
#define ls k<<1
#define rs k<<1|1
#define mid ((l+r)>>1)
inline ll read()
{
ll ret=0;char ch=' ',c=getchar();
while(!(c>='0'&&c<='9')) ch=c,c=getchar();
while(c>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
return ch=='-'?-ret:ret;
}
int n,m;
inline ll qpow(ll x,ll y)
{
ll ret=1;
while(y)
{
if(y&1) ret=ret*x%mod;
x=x*x%mod;
y>>=1;
}
return ret;
}
const int inv = qpow(3,mod-2);
int dfn[N],tim,oud[N],siz[N],mi3;
ll tree[N<<2],lazy[N<<2];
void build(int k,int l,int r)
{
lazy[k]=1;
if(l==r) {tree[k]=mi3;return;}
build(ls,l,mid);
build(rs,mid+1,r);
//tree[k]=tree[ls]*tree[rs]%mod;
}
inline void Add(int k,int l,int r,int v)
{
tree[k]=tree[k]*v%mod;
lazy[k]=lazy[k]*v%mod;
//printf("lazy[k]=%lld
,(%d,%d)
",lazy[k],l,r);
}
inline void pushdown(int k,int l,int r)
{
if(lazy[k]==1) return;
Add(ls,l,mid,lazy[k]);
Add(rs,mid+1,r,lazy[k]);
lazy[k]=1;//懒标记不清空,亲人两行泪
}
void modify(int k,int l,int r,int x,int y,int v)
{
if(x<=l&&r<=y) {Add(k,l,r,v);return;}
pushdown(k,l,r);
if(x<=mid) modify(ls,l,mid,x,y,v);
if(y>mid) modify(rs,mid+1,r,x,y,v);
// tree[k]=tree[ls]*tree[rs]%mod;
}
ll query(int k,int l,int r,int x)
{
// printf("tree[%d]=%d
",k,tree[k]);
if(l==r) return tree[k];
pushdown(k,l,r);
if(x<=mid) return query(ls,l,mid,x);
else return query(rs,mid+1,r,x);
}
int head[N],ecnt=-1;
struct edge
{
int nxt,to;
}a[N<<1];
struct ask
{
int op,u,v;
}q[N];
inline void add(int x,int y)
{
a[++ecnt]=(edge){head[x],y};
head[x]=ecnt;
}
void dfs(int u,int fa)
{
dfn[u]=++tim;siz[u]=1;
for(int i=head[u];~i;i=a[i].nxt)
{
int v=a[i].to;
if(v==fa) continue;
dfs(v,u);
siz[u]+=siz[v];
}
}
int main()
{
memset(head,-1,sizeof(head));
n=read(),m=read();
mi3=qpow(3,n);
build(1,1,n);
for(int i=1;i<=m;i++)
{
q[i].op=read(),q[i].u=read();
if(q[i].op==1) q[i].v=read(),oud[q[i].v]++;
}
for(int i=m;i>=1;i--)//倒序建边日神仙
{
int op=q[i].op,u=q[i].u,v=q[i].v;
if(op==1) add(v,u),add(u,v);
}
for(int i=1;i<=n;i++)
if(!oud[i]) dfs(i,0);
// for(int i=1;i<=n;i++)
// printf("dfn[%d]=%d,siz[%d]=%d
",i,dfn[i],i,siz[i]);
for(int i=1;i<=m;i++)
{
int op=q[i].op,u=q[i].u,v=q[i].v;
if(op==1)
{
modify(1,1,n,dfn[u],dfn[v]-1,2*inv);
modify(1,1,n,dfn[v],dfn[v]+siz[v]-1,inv);
// printf("[%d,%d] [%d,%d]
",dfn[u],dfn[v]-1,dfn[v],dfn[v]+siz[v]-1);
}
else printf("%lld
",query(1,1,n,dfn[u]));
}
return 0;
}
/*
3 5
2 1
1 2 1
2 1
1 2 3
2 1
*/