D.
题意:给定等长(n)01串a,b,每次等概率在a串中反转一个字符,请问两个串变成相同的期望操作次数.
设f[i]为有i个位置不同情况下的期望操作次数,f[i] = (i / n)f[i - 1] + (1 - i / n)f[i +1] + 1,移项得f[i] = (f[i - 1] - 1 - (i - 1) / n * f(i - 2)) / ((n - i + 1) / n).f[0] = 0,那么最后会消剩下f[1],用f[1]表示f[2~n],利用边界条件f[n] = f[n - 1] + 1,计算出f[1].答案就是f[dif](dif为初始a,b串字符不同的位置个数).
#include <bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define ll long long
// #define double long double
#define ull unsigned long long
#define PII pair<int, int>
#define PDI pair<double, int>
#define PDD pair<double, double>
#define debug(a) cout << #a << " = " << a << endl
#define point(n) cout << fixed << setprecision(n)
#define all(x) (x).begin(), (x).end()
#define mem(x, y) memset((x), (y), sizeof(x))
#define lbt(x) (x & (-x))
#define SZ(x) ((x).size())
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
// namespace nqio { const unsigned R = 4e5, W = 4e5; char* a, * b, i[R], o[W], * c = o, * d = o + W, h[40], * p = h, y; bool s; struct q { void r(char& x) { x = a == b && (b = (a = i) + fread(i, 1, R, stdin), a == b) ? -1 : *a++; } void f() { fwrite(o, 1, c - o, stdout); c = o; } ~q() { f(); }void w(char x) { *c = x; if (++c == d) f(); } q& operator >>(char& x) { do r(x); while (x <= 32); return *this; } q& operator >>(char* x) { do r(*x); while (*x <= 32); while (*x > 32) r(*++x); *x = 0; return *this; } template<typename t> q& operator>>(t& x) { for (r(y), s = 0; !isdigit(y); r(y)) s |= y == 45; if (s) for (x = 0; isdigit(y); r(y)) x = x * 10 - (y ^ 48); else for (x = 0; isdigit(y); r(y)) x = x * 10 + (y ^ 48); return *this; } q& operator <<(char x) { w(x); return *this; }q& operator<< (char* x) { while (*x) w(*x++); return *this; }q& operator <<(const char* x) { while (*x) w(*x++); return *this; }template<typename t> q& operator<< (t x) { if (!x) w(48); else if (x < 0) for (w(45); x; x /= 10) *p++ = 48 | -(x % 10); else for (; x; x /= 10) *p++ = 48 | x % 10; while (p != h) w(*--p); return *this; } }qio; }using nqio::qio;
using namespace std;
const int N = 1e6 + 10, MOD = 998244353;
int inv[N];
struct rec {
int a, b;
} f[N];
rec operator + (rec x, rec y) {
return {(x.a + y.a) % MOD, (x.b + y.b) % MOD};
}
rec operator + (rec x, int y) {
return {x.a, (x.b + y) % MOD};
}
rec operator - (rec x, rec y) {
return {(x.a - y.a + MOD) % MOD, (x.b - y.b + MOD) % MOD};
}
rec operator - (rec x, int y) {
return {x.a, (x.b - y + MOD) % MOD};
}
rec operator * (rec x, int y) {
return {x.a * y % MOD, x.b * y % MOD};
}
rec operator / (rec x, int y) {
return {x.a * inv[y] % MOD, x.b * inv[y] % MOD};
}
int qmi(int a, int k) {
int res = 1;
while (k) {
if (k) res = res * a % MOD;
k >>= 1;
a = a * a % MOD;
}
return res;
}
void solve() {
int n;
string a, b;
cin >> n >> a >> b;
int dif = 0;
for (int i = 0; i < n; ++i) {
dif += (a[i] != b[i]);
}
f[0] = {0, 0}, f[1] = {1, 0};
for (int i = 2; i <= n; ++i) {
f[i] = (f[i - 1] - 1 - f[i - 2] * ((i - 1) * inv[n] % MOD)) * (inv[n - i + 1] % MOD * n % MOD);
}
auto t = f[n] - f[n - 1];
int x = (1 - t.b + MOD) % MOD * qmi(t.a, MOD) % MOD;
cout << (f[dif].a * x + f[dif].b) % MOD << "\n";
}
signed main() {
IOS;
inv[0] = inv[1] = 1;
for (int i = 2; i < N; ++ i) {
inv[i] = 1ll * (MOD - MOD / i) * inv[MOD % i] % MOD;
}
int T;
cin >> T;
while (T--) solve();
}
E.
题意:给定一棵n个节点的无根树,每个节点有权值a[i].回答q次询问:给定r,v,若以r为整棵树的根,以v为根的子树中节点权值最大异或和.
以1为根,考虑维护dfn前缀线性基.
1.如果r=v,那么输出整棵树的最大异或和.
2.如果v是r的祖先,那么利用倍增找到v->r路径上v的下一个节点y,然后合并[1,L[y]-1],[R[y]+1,n]的线性基,然后求最大异或和.
3.其他情况直接输出以v为根的子树的线性基即可.
#include <bits/stdc++.h>
// #define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define ll long long
// #define double long double
#define ull unsigned long long
#define PII pair<int, int>
#define PDI pair<double, int>
#define PDD pair<double, double>
#define debug(a) cout << #a << " = " << a << endl
#define point(n) cout << fixed << setprecision(n)
#define all(x) (x).begin(), (x).end()
#define mem(x, y) memset((x), (y), sizeof(x))
#define lbt(x) (x & (-x))
#define SZ(x) ((x).size())
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
// namespace nqio { const unsigned R = 4e5, W = 4e5; char* a, * b, i[R], o[W], * c = o, * d = o + W, h[40], * p = h, y; bool s; struct q { void r(char& x) { x = a == b && (b = (a = i) + fread(i, 1, R, stdin), a == b) ? -1 : *a++; } void f() { fwrite(o, 1, c - o, stdout); c = o; } ~q() { f(); }void w(char x) { *c = x; if (++c == d) f(); } q& operator >>(char& x) { do r(x); while (x <= 32); return *this; } q& operator >>(char* x) { do r(*x); while (*x <= 32); while (*x > 32) r(*++x); *x = 0; return *this; } template<typename t> q& operator>>(t& x) { for (r(y), s = 0; !isdigit(y); r(y)) s |= y == 45; if (s) for (x = 0; isdigit(y); r(y)) x = x * 10 - (y ^ 48); else for (x = 0; isdigit(y); r(y)) x = x * 10 + (y ^ 48); return *this; } q& operator <<(char x) { w(x); return *this; }q& operator<< (char* x) { while (*x) w(*x++); return *this; }q& operator <<(const char* x) { while (*x) w(*x++); return *this; }template<typename t> q& operator<< (t x) { if (!x) w(48); else if (x < 0) for (w(45); x; x /= 10) *p++ = 48 | -(x % 10); else for (; x; x /= 10) *p++ = 48 | x % 10; while (p != h) w(*--p); return *this; } }qio; }using nqio::qio;
using namespace std;
template<class T>struct Basic{
#define MAXSIZE 62
T f[MAXSIZE + 5], g[MAXSIZE + 5];
int flag, sz;
//flag 记录插入时是否有不能放入的数
//局部变量记得clear
void clear() {
memset(f, 0, sizeof f);
flag = sz = 0;
}
void insert(T x){
for (int i = MAXSIZE; i >= 0; --i) {
if (!(x >> i & 1)) continue;
if(!f[i]) {
f[i] = x;
++sz;
return;
}
x ^= f[i];
}
++flag;
}
T queryMax() {
T res = 0;
for (int i = MAXSIZE; i >= 0; --i) {
res = max(res ^ f[i], res);
}
return res;
}
T queryMin(){
if (flag) {
return 0;
}
for (int i = 0; i <= MAXSIZE; ++i) {
if (f[i]) {
return f[i];
}
}
return -1;
}
void rebuild(){
for (int i = 0; i <= MAXSIZE; ++i) {
g[i] = f[i];
}
for (int i = 0; i <= MAXSIZE; ++i) {
for (int j = 0; j <= i - 1; ++j) {
if (g[i] >> j & 1) {
g[i] ^= g[j];
}
}
}
}
T queryNum(T k){
//must build g before use it
if (flag) {
--k;
}
if (k == 0) {
return 0;
}
T res = 0;
for (int i = 0; i <= MAXSIZE; ++i) {
if (g[i]) {
if (k & 1) {
res ^= g[i];
}
k >>= 1;
}
}
return k ? -1 : res;
}
void insert(Basic B) {
for (int i = 0; i <= MAXSIZE; ++i) {
insert(B.f[i]);
}
}
};
const int N = 2e5 + 10;
Basic<int> s[N], pre[N], suf[N];
int n, a[N], sz[N], dfn[N], ndfn[N], dep[N], fa[N][22], tim;
vector<int> g[N];
void dfs(int x, int f, int d) {
sz[x] = 1;
ndfn[dfn[x] = ++tim] = x;
dep[x] = d;
fa[x][0] = f;
s[x].insert(a[x]);
for (int i = 1; i <= __lg(d); ++i) {
fa[x][i] = fa[fa[x][i - 1]][i - 1];
}
for (int y : g[x]) {
if (y == f) {
continue;
}
dfs(y, x, d + 1);
s[x].insert(s[y]);
sz[x] += sz[y];
}
}
int find(int x, int len) {
int y = x;
for (int i = __lg(len); i >= 0; --i) {
if (len >> i & 1) {
y = fa[y][i];
}
}
return y;
}
void solve() {
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
s[i].clear();
g[i].clear();
}
for (int i = 1; i <= n - 1; ++i) {
int x, y;
cin >> x >> y;
g[x].emplace_back(y);
g[y].emplace_back(x);
}
tim = 0;
dfs(1, 1, 1);
for (int i = 1; i <= n; ++i) {
pre[i] = pre[i - 1];
pre[i].insert(a[ndfn[i]]);
}
suf[n + 1].clear();
for (int i = n; i >= 1; --i) {
suf[i] = suf[i + 1];
suf[i].insert(a[ndfn[i]]);
}
int q;
cin >> q;
while (q--) {
int r, v;
cin >> r >> v;
if (r == v) {
cout << s[1].queryMax() << "\n";
}else if (dep[r] <= dep[v] || find(r, dep[r] - dep[v]) != v) {
cout << s[v].queryMax() << "\n";
}else {
Basic<int> ans;
ans.clear();
int k = find(r, dep[r] - dep[v] - 1);
ans.insert(pre[dfn[k] - 1]);
ans.insert(suf[dfn[k] + sz[k]]);
cout << ans.queryMax() << "\n";
}
}
}
signed main() {
IOS;
int T;
cin >> T;
while (T--) solve();
}
F.
题意:给定n个点的以1为根的树,每个点有权值a[i],操作最多k次:选择之前没有被选择的点,让所有点乘以该子树所有权值的约数.问根节点最大权值.
乘以gcd必然是最优的,整颗树的gcd一定是a[1]的约数,考虑树dp,f[x][g]表示x点能够提供gcd=g的最小操作次数.枚举a[1]的因子t,如果不对该点执行操作,那么至多提供gcd(a[1],a[x]);如果执行操作,至多提供g*gcd(a[1]/g,g).继承(y为x的儿子)f[y][t]之后,用后缀子集和处理即可.
#include<bits/stdc++.h>
using namespace std;
using LL = long long;
const int maxn = 1e5 + 5, INF = 1e9;
vector<int> primes[1005];
vector<int> factors[1005];
int a[maxn];
vector<int> g[maxn];
int f[maxn][32];
int id[1005];
vector<int> prime, factor;
int gc[1005][1005];
void dfs(int u, int fa){
for(int i = 0; i < factor.size(); i++) f[u][i] = INF;
for(auto j : g[u]){
if (j == fa) continue;
dfs(j, u);
}
for(int i = 0; i < factor.size(); i++){
int x = factor[i];
LL sum = 0;
for(auto j : g[u]){
if (j == fa) continue;
sum += f[j][i];
}
int g = gc[x][a[u]];
f[u][id[g]] = min(1LL * f[u][id[g]], sum);
int ng = g * gc[a[1] / g][g];
f[u][id[ng]] = min(1LL * f[u][id[ng]], sum + 1);
}
for(int i = factor.size() - 1; i >= 0; i--){
int x = factor[i];
for(auto p : prime)
if (x % p == 0) f[u][id[x / p]] = min(f[u][id[x / p]], f[u][i]);
}
}
signed main(){
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
for(int i = 1; i <= 1000; i++)
for(int j = i; j <= 1000; j += i)
factors[j].push_back(i);
for(int i = 2; i <= 1000; i++){
if (primes[i].empty()){
for(int j = i; j <= 1000; j += i)
primes[j].push_back(i);
}
}
for(int i = 1; i <= 1000; i++)
for(int j = 1; j <= 1000; j++)
gc[i][j] = __gcd(i, j);
int T;
cin >> T;
while(T--){
int n, k;
cin >> n >> k;
for(int i = 1; i <= n; i++) g[i].clear();
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 0; i < n - 1; i++){
int a, b;
cin >> a >> b;
g[a].push_back(b), g[b].push_back(a);
}
prime = primes[a[1]];
factor = factors[a[1]];
for(int i = 0; i < factor.size(); i++)
id[factor[i]] = i;
dfs(1, -1);
for(int i = factor.size() - 1; i >= 0; i--){
LL sum = 0;
for(auto j : g[1])
sum += f[j][i];
if (sum + (i > 0) <= k){
cout << a[1] * factor[i] << '\n';
break;
}
}
}
}