题目

(可能有点长,但是请耐心看完,个人认为比官方题解好懂:P)


首先需要注意,对于任意节点i上的一个棋子,如果在一种走法中它走到了节点j,另一种走法中它走到了节点k,那么这两种走法进行完后,棋子占据的节点集合不可能相同,因为在这两种走法中,节点i必有两个子树中的棋子数量不同。所以,题目中的"被占据的集合唯一"等价于"每个棋子走向的节点唯一"。

根据题意,一个初始状态合法当且仅当这个状态可以进行任意次操作,且进行k步操作后,接下来一步操作唯一(不管这样走之后,是否还能进行无限次操作)。先不考虑"唯一",只考虑怎么使得可以进行无限次操作。我们可以把树分成一些链,每条链初始有一个端点是空的,其他节点都被棋子占据。第一步操作时,把每条链上的每个棋子,都往这条链上的空节点方向移动一步。后面的每一步操作都可以在这两个状态之间反复横跳,满足了"无限次"的要求。现在把"唯一"的条件加进来,发现树上的每个节点都必须恰好被一条链覆盖到,不然肯定存在一个没被覆盖的节点i,使得在某一步中可以把一个本应该走到其他位置的棋子移到这个点上,使得方案不唯一。

如果一个状态合法,唯一的操作方案就是:每条链上的棋子在这条链上左右横跳。现在来看看哪些"链划分"是不合法的(操作方案不唯一)。我们把每条链没有棋子的端点称为"0端",有棋子的端点称为"1端",这两个统称端点;其它点称为中间点。


先给出结论:两个相邻节点x、y,如果出现以下情况之一,这种状态就不合法,否则合法:1.一个是中间点,一个是端点,且属于不同的链;2.两个点都是1端(属于不同的链);3.两个点都是0端(显然也属于不同的链)。

证明:

1.出现以上情况的一定不合法

  1. 如果x是中间点且y是端点,不属于同链,如果y是1则x可以在第一步向y移动,y是0则x可以在第二步向y移动,均不唯一

  2. 都是1端,则可以合并成一条链,并扔掉其中一条链的0端,不唯一

  3. 都是0端,则移动一次后可以合并成一条链

2.不出现的一定合法

只需要证明任意一种没有上述情况的状态,第一步操作都唯一。因为操作一次后达到的状态是与其对称的,再操作一次又回到了这个状态。

考虑一个中间节点会不会不守本分,跑到其他的链去。那肯定是跑到了另一条链的一个中间节点,原本要到这个点的棋子就必须找另一条路,它可以走到另一条链,也可以向着本链的1端走一步,第一种走法循环了,所以若干次后总会走上第二种。走上第二种后,类似的,总能规约到一个1端棋子必须走到别的链(的0端)。原来要走到这个0端节点的棋子是在一个中间位置上的,它又需要走到别的链,或者向本链1端走\(\cdots\)

发现陷入了死循环,所以总有一个点找不到出路,所以方案不唯一的情况此时不会出现。


接下来对合法的链划分计数。考虑树形dp,dp过程中不考虑每条链的方向(即忽略0端和1端的位置)

\[\begin{align}
dp[*][0]&:当前是中间节点,所在链的两端点都在子树内\\
dp[*][1]&:中间节点,链只有一个端点在子树内\\
dp[*][2]&:端点,与之匹配的端点不在子树内\\
dp[*][3]&:端点,匹配的端点在子树内
\end{align}
\]

转移比较简单,不一一赘述。考虑怎么加入每条链方向的影响,如果两条链端点相邻,则在这两条链间连边。最后每一个连通块都有2种方案。对于任意一个中间节点,它在树上的儿子中除了1或2个,其他都跟它属于不同的连通块,当前dp到的节点为中间节点时乘上对应方案数即可。

时间复杂度\(O(n)\)。

点个赞求求了,/kel

点击查看代码
#include <bits/stdc++.h>

#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define pb push_back
#define fi first
#define se second
#define mpr make_pair using namespace std; const LL MOD=998244353; LL qpow(LL x,LL a)
{
LL res=x,ret=1;
while(a>0)
{
if((a&1)==1) ret=ret*res%MOD;
a>>=1;
res=res*res%MOD;
}
return ret;
} LL n,dp[200010][4],dp2[200010][3],suf[200010];
vector <LL> g[200010]; void dfs(LL pos,LL par)
{
vector <LL> son;
rep(i,g[pos].size()) if(g[pos][i]!=par)
{
dfs(g[pos][i],pos);
son.pb(g[pos][i]);
}
//2
dp[pos][2]=1;rep(i,son.size()) (dp[pos][2]*=dp[son[i]][3])%=MOD;
//3
suf[0]=1;rep(i,son.size()) suf[i+1]=suf[i]*dp[son[son.size()-i-1]][3]%MOD;
LL bas=1;
rep(i,son.size())
{
(dp[pos][3]+=(dp[son[i]][1]+dp[son[i]][2])%MOD*bas%MOD*suf[son.size()-i-1])%=MOD;
(bas*=dp[son[i]][3])%=MOD;
}
//1
suf[0]=1;rep(i,son.size()) suf[i+1]=suf[i]*dp[son[son.size()-i-1]][0]%MOD;
bas=1;
rep(i,son.size())
{
(dp[pos][1]+=(dp[son[i]][1]+dp[son[i]][2])%MOD*bas%MOD*suf[son.size()-i-1])%=MOD;
(bas*=dp[son[i]][0])%=MOD;
}
//0
rep(i,son.size()+3) rep(j,3) dp2[i][j]=0;
dp2[0][0]=1;
rep(i,son.size()) rep(j,3)
{
if(j<2) (dp2[i+1][j+1]+=dp2[i][j]*(dp[son[i]][1]+dp[son[i]][2]))%=MOD;
(dp2[i+1][j]+=dp2[i][j]*dp[son[i]][0])%=MOD;
}
dp[pos][0]=dp2[son.size()][2];
LL mul=(LL)son.size()-2;
if(mul>0) (dp[pos][0]*=qpow(2,mul))%=MOD;
++mul;
if(mul>0) (dp[pos][1]*=qpow(2,mul))%=MOD;
} int main()
{
cin>>n;
LL x,y;
rep(i,n-1)
{
scanf("%lld%lld",&x,&y);
g[x].pb(y);g[y].pb(x);
}
dfs(1,0);
cout<<(dp[1][0]+dp[1][3])*2LL%MOD<<endl;
return 0;
}

[题解] Atcoder ARC 142 D Deterministic Placing 结论,DP的更多相关文章

  1. [题解] Atcoder ARC 142 E Pairing Wizards 最小割

    题目 建图很妙,不会. 考虑每一对要求合法的巫师(x,y),他们两个的\(a\)必须都大于\(min(b_x,b_y)\).所以在输入的时候,如果\(a_x\)或者\(a_y\)小于\(min(b_x ...

  2. 【题解】POJ2279 Mr.Young′s Picture Permutations dp

    [题解]POJ2279 Mr.Young′s Picture Permutations dp 钦定从小往大放,然后直接dp. \(dp(t1,t2,t3,t4,t5)\)代表每一行多少人,判断边界就能 ...

  3. 【题解】HDU4689 Derangement(有技巧的计数DP)

    [题解]HDU4689 Derangement(有技巧的计数DP) 传送门 呵呵没告诉我多测组数,然后\(n\le 20,7000\mathrm{ms}\)我写了个状压上去T了 题目大意: 要你求错排 ...

  4. 【题解】Music Festival(树状数组优化dp)

    [题解]Music Festival(树状数组优化dp) Gym - 101908F 题意:有\(n\)种节目,每种节目有起始时间和结束时间和权值.同一时刻只能看一个节目(边界不算),在所有种类都看过 ...

  5. 【题解】CF1056F Write the Contest(三分+贪心+DP)

    [题解]CF1056F Write the Contest(三分+贪心+DP) 最优化问题的三个解决方法都套在一个题里了,真牛逼 最优解应该是怎样的,一定存在一种最优解是先完成了耗时长的任务再干别的( ...

  6. [题解] Atcoder Regular Contest ARC 147 A B C D E 题解

    点我看题 A - Max Mod Min 非常诈骗.一开始以为要观察什么神奇的性质,后来发现直接模拟就行了.可以证明总操作次数是\(O(nlog a_i)\)的.具体就是,每次操作都会有一个数a被b取 ...

  7. 【题解】Atcoder ARC#96 F-Sweet Alchemy

    首先,我们发现每一个节点所选择的次数不好直接算,因为要求一个节点被选择的次数大于等于父亲被选择的次数,且又要小于等于父亲被选择的次数 \(+D\).既然如此,考虑一棵差分的树,规定每一个节点被选择的次 ...

  8. 【题解】Atcoder ARC#90 F-Number of Digits

    Atcoder刷不动的每日一题... 首先注意到一个事实:随着 \(l, r\) 的增大,\(f(r) - f(l)\) 会越来越小.考虑暴力处理出小数据的情况,我们可以发现对于左端点 \(f(l) ...

  9. 【题解】Atcoder ARC#94 F-Normalization

    再次膜拜此强题!神级性质之不可能发现系列收藏++:首先,对于长度<=3的情况,我们采取爆搜答案(代码当中是打表).对于长度>=4的情况,则有如下几条玄妙的性质: 首先我们将 a, b, c ...

随机推荐

  1. Dokcer运行Nacos容器自动退出问题

    Dokcer运行Nacos容器自动退出问题 参考博文 学生党,租的云服务器,2核2G.使用Docker运行Nacos容器的时候发现总是自动退出.Nacos日志里面没有明显的报错信息.查了一下是内存溢出 ...

  2. 【一本通基础DP基础模型】摘花生

    题面 题目描述 Hello Kitty想摘点花生送给她喜欢的米老鼠.她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来.地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生, ...

  3. [mybatis]mybatis日志的使用和分页功能的实现

    日志 Mybatis 通过使用内置的日志工厂提供日志功能.内置日志工厂将会把日志工作委托给下面的实现之一: SLF4J Apache Commons Logging Log4j 2 Log4j JDK ...

  4. python socket理解

    socket 所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象.一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制.从所处的地位来讲 ...

  5. 霜皮剥落紫龙鳞,下里巴人再谈数据库SQL优化,索引(一级/二级/聚簇/非聚簇)原理

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_206 举凡后端面试,面试官不言数据库则已,言则必称SQL优化,说起SQL优化,网络上各种"指南"和" ...

  6. Spring源码 11 IOC refresh方法6

    参考源 https://www.bilibili.com/video/BV1tR4y1F75R?spm_id_from=333.337.search-card.all.click https://ww ...

  7. Git 08 IDEA撤销添加

    参考源 https://www.bilibili.com/video/BV1FE411P7B3?spm_id_from=333.999.0.0 版本 本文章基于 Git 2.35.1.2 如果将不想添 ...

  8. MyBatis 03 缓存

    简介 什么是缓存 存在内存中的临时数据. 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,转从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题 ...

  9. [HFCTF2020]EasyLogin-1|JWT身份伪造

    1.打开之后只有一个登陆界面和注册界面,右键检查发现app.js代码,结果如下: app.js代码如下: /** * 或许该用 koa-static 来处理静态文件 * 路径该怎么配置?不管了先填个根 ...

  10. 算法模板:C++的高精度

    代码是抄别人的:https://blog.csdn.net/code4101/article/details/38705155. 这篇博客只是用来查看保存,非原创. #include<iostr ...