题目

(可能有点长,但是请耐心看完,个人认为比官方题解好懂: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. mybatis-plus时间字段自动填充

    时间代码自动填充的2种方式 数据库方式 将数据库字段create_time和update_time设置CURRENT_TIMESTAMP,create_time字段后面不需要勾选更新,update_t ...

  2. 基于gRPC编写golang简单C2远控

    概述 构建一个简单的远控木马需要编写三个独立的部分:植入程序.服务端程序和管理程序. 植入程序是运行在目标机器上的远控木马的一部分.植入程序会定期轮询服务器以查找新的命令,然后将命令输出发回给服务器. ...

  3. 2501-Logback的使用与配置范例xml

    在项目中logback一般配合slf4j使用,slf4j是面上的框架,logback才是真正记录处理日志的框架. 参考文献: Java日志框架:logback详解 http://www.importn ...

  4. Java异步记录日志-2022新项目

    一.业务场景 web项目开发中,经常会有的一个操作是记录请求日志,比如记录请求的IP地址,记录请求的路径,记录请求的参数等等. 每个系统都会根据自己的需要来记录一些请求相关的日志.一般会将记录的日志信 ...

  5. devops-1:代码仓库git的使用

    devops-gitlab 介绍 gitlab同github.gitee和bitbucket功能一致,都是提供一个存储代码的服务,这里就以gitlab为例,学习一下如何结合git工具使用. 核心组件: ...

  6. EB和Varuxn的单字聊天

    持续更新! 本文已经征得\(Varuxn\)同意,仅当做记录网课的趣事和"深厚"的友情 原标题<ErB和Varuxn的单字聊天> 原标题来源: 这个想法来源是 \(Va ...

  7. FPGA/Verilog 资源整理

    verilog学习教程(以Vivado为载体)https://vlab.ustc.edu.cn/guide/index.html 中科大的数电实验网站https://vlab.ustc.edu.cn/

  8. kubernetes网络模型

    Overview 本文将探讨Kubernetes中的网络模型,以及对各种网络模型进行分析. Underlay Network Model 什么是Underlay Network 底层网络 Underl ...

  9. 没写完。【java】IDEA-调教界面、设置语言、插件的使用

    步骤: 1.官方网站:https://www.jetbrains.com/idea/ (zip)版本:https://download.jetbrains.com.cn/idea/ideaIC-202 ...

  10. c语言_二叉树的建立以及3种递归

    二叉树c语言的实现 二叉树的建立 二叉树的数据结构 typedef struct node{    int data;    struct node* left;    struct node* ri ...