Warning: session_start(): open(/tmp/sess_74b096bfe529391eca45dcafa6fdb05a, O_RDWR) failed: No space left on device (28) in /data/wiki/inc/init.php on line 239

Warning: session_start(): Failed to read session data: files (path: ) in /data/wiki/inc/init.php on line 239

Warning: Cannot modify header information - headers already sent by (output started at /data/wiki/inc/init.php:239) in /data/wiki/inc/auth.php on line 430

Warning: Cannot modify header information - headers already sent by (output started at /data/wiki/inc/init.php:239) in /data/wiki/inc/actions.php on line 38

Warning: Cannot modify header information - headers already sent by (output started at /data/wiki/inc/init.php:239) in /data/wiki/lib/tpl/dokuwiki/main.php on line 12
2020-2021:teams:legal_string:jxm2001:contest:arc_121 [CVBB ACM Team]

用户工具

站点工具


2020-2021:teams:legal_string:jxm2001:contest:arc_121

到此差别页面的链接

后一修订版
前一修订版
2020-2021:teams:legal_string:jxm2001:contest:arc_121 [2021/07/01 11:24]
jxm2001 创建
2020-2021:teams:legal_string:jxm2001:contest:arc_121 [2021/07/01 21:08] (当前版本)
jxm2001
行 60: 行 60:
 ==== 题意 ==== ==== 题意 ====
  
 +给定一棵 $n$ 个结点的树,问有多少个 $1\sim n$ 的排列 $P$ 满足 $p_i$ 不是结点 $i$ 在树上的祖先结点(不包括自己)。
  
 ==== 题解 ==== ==== 题解 ====
 +
 +设 $\text{dp}(u,​i)$ 表示结点 $u$ 为根的子树中钦定 $i$ 个结点的 $p_i$ 是其祖先结点且该祖先结点位于结点 $u$ 为根的子树的方案数。
 +
 +$\text{dp}$ 要求祖先结点必须位于结点 $u$ 为根的子树是为了防止子树背包合并时的冲突,另外不考虑非钦定结点的 $p_i$ 对方案的贡献,留在最后处理。
 +
 +首先进行树形背包合并,得到不使用 $u$ 的方案数。接下来考虑用 $u$ 来钦定一个结点,得到状态转移
 +
 +$$
 +\text{dp}(u,​i)\gets (\text{sz}(u)-i)\text{dp}(u,​i-1)
 +$$
 +
 +根据容斥定理,最终答案为
 +
 +$$
 +\sum_{i=0}^n(-1)^i(n-i)!\text{dp}(u,​i)
 +$$
 +
 +时间复杂度 $O(n^2)$。
  
 <hidden 查看代码>​ <hidden 查看代码>​
 <code cpp> <code cpp>
 +const int MAXN=2005,​Mod=998244353;​
 +struct Edge{
 + int to,next;
 +}edge[MAXN<<​1];​
 +int head[MAXN],​edge_cnt;​
 +void Insert(int u,int v){
 + edge[++edge_cnt]=Edge{v,​head[u]};​
 + head[u]=edge_cnt;​
 +}
 +int dp[MAXN][MAXN],​temp[MAXN],​sz[MAXN],​frac[MAXN];​
 +void dfs(int u){
 + dp[u][0]=sz[u]=1;​
 + for(int i=head[u];​i;​i=edge[i].next){
 + int v=edge[i].to;​
 + dfs(v);
 + _rep(j,​0,​sz[u])_rep(k,​0,​sz[v])
 + temp[j+k]=(temp[j+k]+1LL*dp[u][j]*dp[v][k])%Mod;​
 + sz[u]+=sz[v];​
 + _rep(j,​0,​sz[u]){
 + dp[u][j]=temp[j];​
 + temp[j]=0;​
 + }
 + }
 + for(int i=sz[u]-1;​i>​=0;​i--)
 + dp[u][i+1]=(dp[u][i+1]+1LL*dp[u][i]*(sz[u]-i-1))%Mod;​
 +}
 +int main()
 +{
 + int n=read_int();​
 + _rep(i,​2,​n)
 + Insert(read_int(),​i);​
 + dfs(1);
 + int ans=0;
 + frac[0]=1;
 + _for(i,​1,​MAXN)
 + frac[i]=1LL*frac[i-1]*i%Mod;​
 + _rep(i,​0,​n){
 + if(i&​1)
 + ans=(ans-1LL*dp[1][i]*frac[n-i])%Mod;​
 + else
 + ans=(ans+1LL*dp[1][i]*frac[n-i])%Mod;​
 + }
 + ans=(ans+Mod)%Mod;​
 + enter(ans);​
 + return 0;
 +}
 +</​code>​
 +</​hidden>​
  
 +===== F - Logical Operations on Tree =====
 +
 +==== 题意 ====
 +
 +给定一棵 $n$ 个结点的树,对每个点给定权值 $0$ 或 $1$,对每个边给定运算 $\text{And}$ 或 $\text{Or}$。
 +
 +问有多少种方案使得下述条件成立:
 +
 +存在合法操作序列,每次操作取一条边,对边相连的两个点进行缩点操作,新点的权值为这两个点的权值做边对应的运算。
 +
 +使得最后只剩下一个点,且该点权值为 $1$。
 +
 +==== 题解 ====
 +
 +考虑与叶子结点相连的边对应的运算以及叶子结点的权值:
 +
 +  - 操作为 $\text{Or}$,叶子结点权值为 $1$:最后处理这条边,一定可以得到只剩下一个点,且该点权值为 $1$ 的情况。
 +  - 操作为 $\text{Or}$,叶子结点权值为 $0$:该操作没有影响,不妨最先处理。
 +  - 操作为 $\text{And}$,叶子结点权值为 $1$:该操作没有影响,不妨最先处理。
 +  - 操作为 $\text{And}$,叶子结点权值为 $0$:该操作一定会使父结点权值变为 $0$,不难发现最先处理最优。
 +
 +显然如果存在第一种情况那一定是合法方案,否则优先处理叶子结点一定是最优方案之一。
 +
 +设 $\text{f}(u)$ 表示以 $u$ 为根的有根树按叶子结点依次处理的过程中一定不会出现第一种情况的方案数,$\text{g}(u)$ 表示在 $f(u)$ 中的合法方案数。
 +
 +对 $f(u)$,首先 $u$ 权值任选,对应 $2$ 种方案。
 +
 +然后对每个子结点 $v$,由于优先处理叶子结点,所以最后对 $g(v)$ 种方案,选取 $u \to v$ 这条边时 $v$ 最终权值一定是 $1$。
 +
 +对 $f(v)-g(v)$,选取 $u \to v$ 这条边时 $v$ 最终权值一定是 $0$。
 +
 +第二、三、四种情况的总贡献为 $f(v)-g(v)+g(v)+f(v)-g(v)=2f(v)-g(v)$,于是有 $f(u)=2\prod_{v\in \text{son}(u)} \left(2f(v)-g(v)\right)$。
 +
 +对 $g(u)$,由于不存在第一种情况,而且必须为合法方案,易知 $u$ 权值必须为 $1$。
 +
 +第四种情况非法,仅考虑第二、三种情况的贡献为 $f(v)-g(v)+g(v)=f(v)$,于是有 $f(u)=\prod_{v\in \text{son}(u)}f(v)$。
 +
 +根据容斥,最后答案为 $2^{2n-1}-f(1)+g(1)$。总时间复杂度为 $O(n)$。
 +
 +<hidden 查看代码>​
 +<code cpp>
 +const int MAXN=1e5+5,​mod=998244353;​
 +struct Edge{
 + int to,next;
 +}edge[MAXN<<​1];​
 +int head[MAXN],​edge_cnt;​
 +void Insert(int u,int v){
 + edge[++edge_cnt]=Edge{v,​head[u]};​
 + head[u]=edge_cnt;​
 +}
 +int f[MAXN],​g[MAXN];​
 +void dfs(int u,int fa){
 + f[u]=2,​g[u]=1;​
 + for(int i=head[u];​i;​i=edge[i].next){
 + int v=edge[i].to;​
 + if(v==fa)continue;​
 + dfs(v,u);
 + f[u]=1LL*f[u]*(2LL*f[v]-g[v])%mod;​
 + g[u]=1LL*g[u]*f[v]%mod;​
 + }
 +}
 +int quick_pow(int a,int k){
 + int ans=1;
 + while(k){
 + if(k&​1)ans=1LL*ans*a%mod;​
 + a=1LL*a*a%mod;​
 + k>>​=1;​
 + }
 + return ans;
 +}
 +int main()
 +{
 + int n=read_int();​
 + _for(i,​1,​n){
 + int u=read_int(),​v=read_int();​
 + Insert(u,​v);​
 + Insert(v,​u);​
 + }
 + dfs(1,0);
 + int ans=(0LL+quick_pow(2,​2*n-1)-f[1]+g[1])%mod;​
 + enter((ans+mod)%mod);​
 + return 0;
 +}
 </​code>​ </​code>​
 </​hidden>​ </​hidden>​
2020-2021/teams/legal_string/jxm2001/contest/arc_121.1625109892.txt.gz · 最后更改: 2021/07/01 11:24 由 jxm2001