首页 > 其他分享 >Atcoder ARC140D One to One

Atcoder ARC140D One to One

时间:2024-03-21 21:35:12浏览次数:23  
标签:Atcoder 连通 方案 int siz fa maxn ARC140D

考虑到对于 \(n\) 个点的连通块,那就有 \(n\) 条边,就是个基环树。
如果这里面有 \(1\) 个 \(-1\),那么就是 \(n - 1\) 条边,就是一棵树。
如果有 \(2\) 个 \(-1\),\(n - 2\) 条边一定不连通,不可能出现。

令 \(-1\) 的个数为 \(c\)。
那么先对于已知的边连上,如果一个连通块是基环树就直接加上 \(n^c\) 即可。
对于剩下的树,设有 \(m\) 颗,子树大小为 \(siz_i\)。

考虑到一棵树如果接到基环树上那么连通块个数不会变。
所以只有当树内部连成基环树时能多一个连通块。
同时这个基环树也可以当作先拼成环,然后其他的树连上来。

于是就只需要统计这 \(k\) 颗树连成环的方案数了。
考虑如果 \(i\) 在环里,那么只需要连到 \(siz_i\) 这些点就行了。
于是假如环的边的端点选取的方案数就是 \(\prod siz_i\)。
还有环的排列方案数,假设有 \(c\) 个点,考虑固定一个点,对于它来说有 \(c - 1\) 种方案数,对于第二个点就有 \(c - 2\) 种,以此类推,方案数为 \((c - 1)!\)。
同时对于剩下 \(m - c\) 颗树可以随便选,\(n^{m - c}\)。
所以令选出来的树为 \(p_{1\sim c}\),方案数即为 \(\prod\limits_{i = 1}^c siz_{p_i}\times (c - 1)!\times n^{m - c}\)。

对于前面的 \(\prod\limits_{i = 1}^c siz_{p_i}\) 可以通过背包得到。
即设 \(f_i\) 为选了 \(i\) 颗树的方案数,就有转移 \(f'_i = f_i + f_{i - 1}\times siz\)。

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

代码
#include<bits/stdc++.h>
using i64 = long long;
const i64 mod = 998244353;
const int maxn = 2e3 + 10;
int fa[maxn], siz[maxn], vis[maxn];
inline int getfa(int x) {return x == fa[x] ? x : (fa[x] = getfa(fa[x]));}
int to[maxn];
i64 f[maxn], fac[maxn], pw[maxn];
int main() {
    int n; scanf("%d", &n);
    std::iota(fa, fa + n + 1, 0);
    for (int i = 1; i <= n; i++) scanf("%d", &to[i]), to[i] != -1 && (fa[getfa(i)] = getfa(to[i]));
    int c = 0;
    for (int i = 1; i <= n; i++) siz[getfa(i)]++, to[i] == -1 && (vis[getfa(i)] = 1, c++);
    pw[0] = fac[0] = 1;
    for (int i = 1; i <= c; i++) pw[i] = pw[i - 1] * n % mod;
    for (int i = 1; i <= c; i++) fac[i] = fac[i - 1] * i % mod;
    f[0] = 1;
    i64 ans = 0;
    for (int i = 1; i <= n; i++) if (i == getfa(i)) {
        if (! vis[i]) {(ans += pw[c]) %= mod; continue;}
        for (int j = c; j; j--) (f[j] += f[j - 1] * siz[i]) %= mod;
    }
    for (int i = 1; i <= c; i++) (ans += f[i] * fac[i - 1] % mod * pw[c - i]) %= mod;
    printf("%lld\n", ans);
    return 0;
}

标签:Atcoder,连通,方案,int,siz,fa,maxn,ARC140D
From: https://www.cnblogs.com/rizynvu/p/18088281

相关文章

  • AtCoder Beginner Contest 345
    A-Leftrightarrow(abc345A)题目大意给定一个字符串,问是不是形如<======...====>的字符串。解题思路根据长度构造出期望的字符串,再判断是否相等即可。神奇的代码s=input()print("Yes"ifs=="<"+"="*(len(s)-2)+">"else"No")B-Inte......
  • AtCoder Beginner Contest 345
    A-Leftrightarrow#include<bits/stdc++.h>usingnamespacestd;usingi32=int32_t;usingi64=longlong;usingldb=longdouble;#defineintlonglongusingvi=vector<int>;usingpii=pair<int,int>;constintmod=1e9+7;......
  • AtCoder Beginner Contest 345 A-F 题解
    A-LeftrightarrowQuestion给你一个由<、=和>组成的字符串\(S\)。请判断\(S\)是否是双向箭头字符串。字符串\(S\)是双向箭头字符串,当且仅当存在一个正整数\(k\),使得\(S\)是一个<、\(k\)个=和一个>的连接,且顺序如此,长度为\((k+2)\)。Solution按照题意......
  • Atcoder ARC165D Xor Sum 5
    考虑到这个最终的答案是\(\oplus\),所以只需要考虑每种排列出现的次数的奇偶,如果为奇再计算贡献即可。考虑什么情况下出现次数为奇。令每个数出现的个数为\(c_{1\simn}\),方案数即为\(\dbinom{k}{c_1,c_2,\cdots,c_n}=\prod_{i=1}^n\dbinom{k-\sum\limits_{j=1}^{......
  • Monoxer Programming Contest 2024(AtCoder Beginner Contest 345)
    MonoxerProgrammingContest2024(AtCoderBeginnerContest345)\(A\)Leftrightarrow\(100pts\)按照题意模拟即可。点击查看代码intmain(){strings;inta=0,b=0,c=0,i;cin>>s;for(i=0;i<s.size();i++){a+=(s[i]=='<&#......
  • AtCoder-abc345_f题解
    题意简述给定一个无向图。你要在其中选出一些边,使得选出的边所构成的图中,度数为奇数的点有\(K\)个。如果可以,输出选了哪些边,否则输出-1。思路首先在选一条边时,边两端点度数的奇偶性一定都会改变,即要么都变为奇数,要么两个点的奇偶性交换过来,要么都变为偶数。这三种情况时满足......
  • AtCoder Beginner Contest 345
    是这样的,C的longlong只开了ans没开全局,AC12WA12,调了一个小时,在赛后1min发现了该错误。没开longlong见祖宗,望周知;这是C的码,简单的小题一只,可怜的longlong。#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;strings;intn,f,ans;map<char......
  • AtCoder Grand Contest 022 E Median Replace
    洛谷传送门AtCoder传送门考虑对于一个确定的串怎么判断合法性。容易发现删到某个时刻若\(1\)的个数大于\(0\)的个数了,因为我们肯定不会蠢到在不是全\(1\)的时候删\(111\),所以\(c_1-c_0\)在不是全\(1\)的时候至少是不会变小的。所以我们的目标就是让\(c_1-c_0......
  • AtCoder Beginner Contest 344 A-G 题解
    AtCoderBeginnerContest344A-SpoilerQuestion删除两个|之间的字符Solution按照题意模拟即可Code#include<bits/stdc++.h>usingnamespacestd;intmain(){strings;cin>>s;stringp1,p2;for(inti=0;i<s.size();i++){......
  • Toyota Programming Contest 2024#3(AtCoder Beginner Contest 344)
    C先预处理出三个数组能拼出的数,存放到map中。查询的时候只需要看这个数是否出现在map里即可。时间复杂度\(O(n^3\logv+Q\logv)\),\(n\leq100\),\(\logv\)是map的时间复杂度。#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongconstintN=3e......