AT_arc156_e ARC156E Non-Adjacent Matching
原题链接:ARC156E Non-Adjacent Matching
分析
这怎么刻画?
好像可以直接容斥......就是我们看有几个点连成了 i i i 和 i + 1 i+1 i+1 之间的环......这个是不是可以 DP?那,是不是做完了?
难道我想简单了?我觉得那样的错的......还是 DP。这个 n 2 n^2 n2 是显著的。怎么设状态呢?
那么,我设 d p i , j dp_{i,j} dpi,j 表示考虑了前 i i i 个位置,有 j j j 个形如 k k k 连向 k + 1 k+1 k+1 的边的方案数。
d p i , j = ∑ k = 0 i − 1 ( − 1 ) k × d p i − 1 , k dp_{i,j}=\sum\limits_{k=0}^{i-1}{(-1)^k\times dp_{i-1,k}} dpi,j=k=0∑i−1(−1)k×dpi−1,k
答案有:
a n s = ( m + 1 ) n × ∑ k = 1 i − 1 d p n , k ans=(m+1)^n\times\sum\limits_{k=1}^{i-1}{dp_{n,k}} ans=(m+1)n×k=1∑i−1dpn,k
正能对我吃啊......
😭
我居然只考虑了一个条件😭
对于和不超过 K K K,那么,我们的总方案数应该是:
∑ i = 0 K C i + n − n × m n \sum\limits_{i=0}^{K}{C_{i+n-n\times m}^{n}} i=0∑KCi+n−n×mn
我们设 d p i , j dp_{i,j} dpi,j 表示考虑了前 i i i 个点,第 i i i 个点的度数为 j j j 的时候的方案数。
这个怎么转移呢?
感觉和上面的差不多......
决定观看题解......
???
这是啥啊😭
首先,度数和必须为奇数......这是为啥啊?后面提到,如果 ∃ i \exists i ∃i,使得 2 × ( x i + x i + 1 ) > ∑ i = 1 n x i 2\times(x_i+x_{i+1})>\sum\limits_{i=1}^{n}x_i 2×(xi+xi+1)>i=1∑nxi,那么就不合法了......将这两个点若看成一个,则必有自环......哦,这个是可以理解的。
那么,求所有在 0 , m 0,m 0,m 就是 [ 0 , ∞ ) − [ m + 1 , + ∞ ) [0,\infty)-[m+1,+\infty) [0,∞)−[m+1,+∞)。
奇数就是背包。
然后,我们还需要去重,由于两块必有交,所以,枚举中点左右,并减去中点即可。
此时,我们只需要第一部分减第二部分加第三部分即可。
这是啥啊😭
正解
cpp
#include <bits/stdc++.h>
//#define int long long
#define mod 998244353
using namespace std;
const int N = 3005, M = 10000005;
int n, m, k;
int fac[M], inv[M];
int sum[M][2], dp1[N][N << 1][2], dp2[N << 1][2];
int qpow(int a, int b){
int res = 1;
while (b){
if (b & 1)
res = 1ll * res * a % mod;
a = 1ll * a * a % mod;
b >>= 1;
}
return res;
}
int C(int n, int m){
if (n < 0 || m < 0 || n < m)
return 0;
return 1ll * fac[n] * inv[n - m] % mod * inv[m] % mod;
}
void work_bag(int p){
for (int i = p; i < n; i++){
int sum[] = {0, 0};
for (int j = m * 2; j >= 0; j--){
swap(sum[0], sum[1]);
sum[0] = (sum[0] + dp1[i][j][0]) % mod;
sum[1] = (sum[1] + dp1[i][j][1]) % mod;
if (j + m < m * 2){
sum[(m + 1) & 1] = (sum[(m + 1) & 1] - dp1[i][j + m + 1][0] + mod) % mod;
sum[((m + 1) & 1) ^ 1] = (sum[((m + 1) & 1) ^ 1] - dp1[i][j + m + 1][1] + mod) % mod;
}
dp1[i + 1][j][0] = sum[0];
dp1[i + 1][j][1] = sum[1];
}
}
}
int ans1, ans2, ans3;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> k;
fac[0] = 1;
for (int i = 1; i < M; i++){
fac[i] = 1ll * fac[i - 1] * i % mod;
}
inv[M - 1] = qpow(fac[M - 1], mod - 2);
for (int i = M - 2; i >= 0; i--){
inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
}
sum[0][0] = 1;
sum[0][1] = 0;
for (int i = 1; i <= k; i++){
int tmp = C(i + n - 1, n - 1);
sum[i][0] = (sum[i - 1][0] + (i & 1 ? 0 : tmp)) % mod;
sum[i][1] = (sum[i - 1][1] + (i & 1 ? tmp : 0)) % mod;
}
for (int i = 0, a = 1; i <= n && i * (m + 1) <= k; i++){
ans1 = (ans1 + 1ll * a * C(n, i) * sum[k - i * (m + 1)][i * (m + 1) & 1]) % mod;
a *= (-1);
}
memset(dp1, 0, sizeof(dp1));
for (int i = 0; i <= m; i++){
for (int j = 0; j <= m; j++){
if (i + j <= k && i + j > 0){
dp1[2][min(k - i - j, i + j - 1)][(i + j) & 1]++;
}
}
}
work_bag(2);
for (int i = 0; i <= m * 2; i++){
ans2 = (ans2 + dp1[n][i][0]) % mod;
}
memset(dp1, 0, sizeof(dp1));
for (int i = 0; i <= m; i++){
for (int j = 0; j <= m; j++){
int l = abs(i - j) + 1, r = min(k - i - j, m);
if (l < 0 || l > r)
continue;
int mid = (abs(i - j) + 1 + k - i - j) >> 1;
if (l <= mid){
int L = l, R = min(mid, r);
dp2[L - abs(i - j) - 1][(i + L + j) & 1]++;
dp2[R - abs(i - j)][(i + (R + 1) + j) & 1]--;
}
if (mid < r){
int L = max(mid + 1, l), R = r;
dp2[k - i - R - j][(i + R + j) & 1]++;
dp2[k - i - L - j + 1][(i + (L - 1) + j) & 1]--;
}
}
}
for (int i = 0; i <= m * 2; i++){
dp1[3][i][0] = dp2[i][0];
dp1[3][i][1] = dp2[i][1];
dp2[i + 1][1] += dp2[i][0];
dp2[i + 1][0] += dp2[i][1];
}
work_bag(3);
for (int i = 0; i <= m * 2; i++){
ans3 = (ans3 + dp1[n][i][0]) % mod;
}
int tmp = ((ans2 - ans3) % mod + mod) % mod;
cout << ((ans1 - 1ll * tmp * n % mod) % mod + mod) % mod;
}
LGP17501 ICPC 2026 Wuhan I Nailoong vs. Bombloong 2
原题链接:ICPC 2026 Wuhan I Nailoong vs. Bombloong 2
分析
我只会 2 n 2n 2n 次询问😭这并没有意义......
中午睡觉之前好像觉得链能做?不过梦到的好像不是这道题。
你谷又更新了?
但是,又有一个问题就是,如何确定这个 d e g deg deg 序列。
原来初始全部为白色吗?那不是好做多了,泪目了😭
但是,我们还是只会做 n n n 次......从某些角度而言,根节点似乎不用......
我们假设收到的序列是 a a a,那么,第 i i i 个点的儿子数量就是 a i − a i − 1 + 1 a_i-a_{i-1}+1 ai−ai−1+1。
然后,确实,一些根节点是可以不算的,那么,解决了......
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 300005;
int n, m;
vector<int> e[N];
int deg[N];
int sz[N];
int a[N];
void dfs1_milk(int u, int fa){
sz[u] = 1;
for (auto v : e[u]){
if (v == fa)
continue;
dfs1_milk(v, u);
sz[u] += sz[v];
}
}
vector<int> ask;
bool cmp(int u, int v){
return sz[u] > sz[v];
}
void dfs2_milk(int u, int fa){
sort(e[u].begin(), e[u].end(), cmp);
ask.push_back(u);
for (auto v : e[u]){
if (v == fa)
continue;
dfs2_milk(v, u);
}
}
void solve_milk(){
cin >> n;
for (int i = 1, u, v; i < n; i++){
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
deg[u]++;
deg[v]++;
}
int mx = 0, p = -1;
for (int u = 1; u <= n; u++){
int cnt = 0;
for (auto v : e[u]){
if (deg[v] == 1)
cnt++;
}
if (cnt > mx){
mx = cnt;
p = u;
}
}
dfs1_milk(p, 0);
dfs2_milk(p, 0);
cout << n - 3 << '\n';
for (int i = 0; i < n - 3; i++){
cout << ask[i] << " ";
}
cout << '\n';
return ;
}
vector<int> g[N];
int siz[N];
int pos;
bool dfs_boom(int u){
while (siz[u] > 0){
siz[u]--;
if (pos == m){
siz[u]++;
return false;
}
pos++;
g[u].push_back(pos);
if (!dfs_boom(pos))
return false;
}
return true;
}
void solve_boom(){
cin >> n >> m;
for (int i = 1; i <= m; i++){
cin >> a[i];
}
for (int i = 1; i <= m; i++){
if (i == 1)
siz[i] = a[i];
else
siz[i] = a[i] - a[i - 1] + 1;
}
pos = 1;
dfs_boom(1);
for (int i = 1; i <= n - 3; i++){
while (siz[i] > 0){
siz[i]--;
g[i].push_back(++pos);
}
}
if (pos < n){
g[pos].push_back(pos + 1);
}
for (int u = 1; u <= n; u++){
for (auto v : g[u]){
cout << u << " " << v << '\n';
}
}
}
int opt;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> opt;
if (opt == 1){
solve_milk();
}
else{
solve_boom();
}
}
千万不要读错题!!!