Codeforces Round #848 (Div. 2)

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;
            }
        }
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值