cf地址
题解先写我切(补)的,这场有点手生(都怪辣鸡实训)
C. Brave Seekers of Unicorns-dp+位运算
题意:
给定数字n,现从1至n,选择若干整数(正序)组成数组,要满足数组{ak}中任意位置 :
ai^ a(i+1) ^a(i+2)!=0
求共有多少种取法(mod 998244353,n数量级1e6)
题解:
先想到用dp[i]记录到i(数字)共有多少种选法
假设现在选择i,j,k,使得i>j>k,从中选出所有满足 i^ j^ k !=0,可以转化为选择所有i与j,并选出(剔除)k 其中k满足 i^ j =k(即 i ^ j^ k==0) 则有dp[i]=∑(dp[[j]-dp[k])
现在要求出所有的k,使i^j =k且i>j>k,这时我们可以将问题转化为,求k使i^ k=j,具体证明如下:
假设现在对于i, 其b0,b1,b2…bm位(从高到低)为1,即i=10010110…
如何找出一个k使k^ i<i,我们知道,对于所有小于i的数j, 其j在 某bx位置上一定为0:
即 i: 100010100… k: 00001xxxx…使k的最高位为b1,,有i^k=10000xxxx…一定是小于i的,此时我们就算是找到了一个k
知对于每一个i^j对应的k,反过来就有对应的j,所以对于所有的k,寻找完毕也就一定对应所有的j,所以我们只需在i的除最高位每一个为1的bit上遍历即可。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const ll N = 1e6 + 7, mod = 998244353;
ll n, dp[N], sum[N];
ll lowbit(ll x) { return x & -x; }
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n;
for (ll i = 1; i <= n; i++) {
ll x = i;
dp[i] = sum[i - 1] + 1;
while (x-lowbit(x)) {
ll now = lowbit(x);
x -= now;
dp[i] = (dp[i] - (sum[(now << 1) - 1] - sum[now - 1]) + mod) % mod;
}
sum[i] = (sum[i - 1] + dp[i]) % mod;
}
cout << sum[n];
}
D. Bank Security Unification-dp+bit
题意:
给定一串数{an},现在从中选出一个子序列(不是连续的){bk}
使得∑bi&b(i+1) 最大
输出最大值
题解:
这题也是dp,dp[i]表示以ai为末尾的子序列的最大值
递推公式:
dp[n]=max(dp[k]+ak&an)
现在通过减少对k的遍历来降低复杂度:
这里我们先简单模拟一下优化思路:
假设现在有 an=10000000, a(n-1)=01000000, a(n-2)=00100000…现有 ak=10010110, 当ak的所有为1的位,对应的 ai (i>k) 都有ai&ak=ai (即对于ai所有为1的位,都包含在 ak为1的位时),对于满足条件的 ai,先设有 an (n>i) 总有 an&ai+ ai&ak>=an&ak
证明:
对于 an & ak 这里我们只考虑两数同时为1的位,那么an&ak<=min(an,ak) 现在我们从所有满足上述条件的ai中取最大的(即包含 ak 的最高为1的位),这时若 an>=ak an&ak=ak,那么
an&ai>=(1<<k的最高位1) ai&ak>=(1<<k的最高位1)
有an&ai+ai&ak>=an&ak,
反之若an<ak,我们可以取an的最高位,以此类推
现在算法为每次按位查找,寻找有相同位的最近数字,
但是这个有点麻烦
我们可以直接记录每位上为1的最后出现的数字,每次dp遍历所有位即可。
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const ll inf = 1e12 + 10, n = 1e6 + 10;
ll n, dp[n], a[n];
ll nxt[50];
ll maxa, max_bit;
ll ans;
void getbit(ll x,ll i) {
ll bit =0;
while (x) {
bit++;
if (x & 1) {
nxt[bit] = i;
}
x >>= 1;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n;
for (ll i = 1; i <= n; i++) {
cin >> a[i];
maxa = max(a[i], maxa);
}
while (maxa) {
max_bit++;
maxa >>= 1;
}
max_bit++;
for (ll i = 1; i <= n; i++) {
for (ll j = 1; j <= max_bit; j++) {
if (nxt[j]) {
dp[i] = max(dp[i], dp[nxt[j]] + (a[i] & a[nxt[j]]));
}
}
getbit(a[i], i);
}
for (ll i = 1; i <= n; i++) {
ans = max(ans, dp[i]);
}
cout << ans;
}
G. Biological Software Utilities-树+组合数
哇这道题我们队最后才切,主要是不知道公式
题意:
给定结点数n,现在要构造一棵无根无向树,满足去除某些边(n/2-1)后,剩下的节点和边(n/2)构成二分图完美匹配
问有多少种构造方法,mod 998244353.
题解:
根据题意,构造树时每次同时添加两个节点,切新加的两个结点为父子(相连)关系,找出所有方法即可。
那么题目就变成了:
1.构造一棵无根树,结点数为n/2,即每个大结点包含两个相连的结点
2.对于整棵树内的结点,计算分配与连接方式
好的,现在上公式:
cayley定理:有n个标志节点的树的数目等于n^(n−2)
将n个点两两组合,种类有f[n],其中f[n]=(n-1)*f[n-2]
然后对于每一个“大边”(即连接大节点之间的边),有四种连接方式:结点1(1,2),结点2(3,4),有1–3,1–4, 2–3, 2–4.
所以整合公式为:
ans=(n/2)^(n/2-2) * f[n] * 4^ (n/2-1)
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const ll N = 1e6 + 7, mod = 998244353;
ll n, f[N], c[N],p[N], ans;
int main() {
cin >> n;
if (n & 1) {
cout << 0;
return 0;
}
f[2] = 1; c[0] = 1; p[1] = 2; p[0] = 1;
for (ll i = 1; i <= n / 2; i++) {
p[i] = p[i - 1] * (n / 2) % mod;
c[i] = 4 * c[i - 1] % mod;
}
for (ll i = 4; i <= n; i += 2) {
f[i] = ((i - 1) * f[i - 2]) % mod;
}
ans = ((f[n] * c[n / 2 - 1]) % mod * p[n / 2 - 2]) % mod;
if (n == 2)ans = 1;
cout << ans;
}
J. Burnished Security Updates
题意:
给出边和结点,要求染色,即结点和相邻的必须染成不同颜色,求某种颜色染色数量最小的方案
题解:
签到题,bfs染色,无了
#include<vector>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=3e5 + 10;
int n, m, ans, c[N];
vector<int>e[N];
bool bfs(int u) {
queue<int>que;
c[u] = 1;
que.push(u);
int c1 = 1, c2 = 0;
while (!que.empty()) {
int f = que.front();
que.pop();
for (auto x : e[f]) {
if (c[x] == 1 && c[f] == 1)return false;
if (c[x] == 2 && c[f] == 2)return false;
if (c[x])continue;
if (c[f] == 1) { c[x] = 2; c2++; }
else if (c[f] == 2) { c[x] = 1; c1++; }
que.push(x);
}
}
ans += min(c1, c2);
return true;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> m;
int f, t;
for (int i = 0; i < m; i++) {
cin >> f >> t;
e[f].push_back(t);
e[t].push_back(f);
}
for (int i = 1; i <= n; i++) {
if (!c[i]) {
if (bfs(i));
else {
cout << -1 << endl;
return 0;
}
}
}
cout << ans;
}