2020 CCPC Wannafly Winter Camp Day2 Div.1&2

依然范特西╮ 提交于 2020-01-30 22:09:31

A
数组sum表示前i个字符中元音的数量,即前缀和
我们处理出每个长度的期望,再称上概率
令数组f[i]表示长度为i的子串中元音的总数,f[1]=1,2,3,...,n[a[i]=]=sum[n]f[1]=1,2,3,...,n[a[i]=元音]=sum[n]f[2]=(1,2)+(2,3)+...+(n1,n)=sum[n]+sum[n]sum[1]=f[1]+sum[n]sum[1]f[2]=(1,2)+(2,3)+...+(n-1,n)=sum[n]+sum[n]-sum[1]=f[1]+sum[n]-sum[1]f[i]=f[i1]+sum[ni+1]sum[i1]f[i]=f[i-1]+sum[n-i+1]-sum[i-1]

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
char str[maxn];
int main()
{
	scanf("%s", str + 1);
	int n = strlen(str + 1);
	vector<ll> sum(n + 1), f(n + 1);
	for (int i = 1; i <= n; ++i) {
		sum[i] = sum[i - 1];
		if (str[i] == 'a' || str[i] == 'e' || str[i] == 'i' || str[i] == 'o' || str[i] == 'u' || str[i] == 'y') 
			sum[i]++;
	}
	for (int i = 1; i <= n; ++i)
		f[i] = f[i - 1] + sum[n - i + 1] - sum[i - 1];

	long double ans = 0;
	for (int i = 1; i <= n; ++i)
		ans += (1.0 * f[i] / i);
	ans *= 2;
	ans /= 1ll * n * (n + 1);
	printf("%.10Lf\n", ans);
}

C
nim游戏变形,令x表示为前i个数的异或和,答案即是满足y<x^y的y数量
分析y<x^y,发现只有当x的二进制出现第一个1时(由大到小)会决定该数是否满足答案;若y的该位也为1,则x^y会小于y;该位为0时,x^y的该位为1大于y

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
const int digit = 60 + 2;
vector<ll> v;
int tr[maxn << 1][digit];
int lowbit(int x) {
	return x & -x;
}
void add(int i, int n, ll val) {
	while (i <= n) {
		for (int j = 0; j < digit; ++j)
			if ((val >> j) & 1)
				tr[i][j]++;
		i += lowbit(i);
	}
}
int sum(int i, int k) {
	int rhs = 0;
	while (i) {
		rhs += tr[i][k];
		i -= lowbit(i);
	}
	return rhs;
}
int main()
{
	ios::sync_with_stdio(false); cin.tie(0);

	int n; cin >> n;
	v.resize(n + 1);
	for (int i = 1; i <= n; ++i) {
		cin >> v[i];
		add(i, n, v[i]);
	}
	ll xor_val = 0;
	for (int i = 1; i <= n; ++i) {
		xor_val ^= v[i];
		int k = -1;
		for (int j = digit; j >= 0; --j) {
			if ((xor_val >> j) & 1) {
				k = j;
				break;
			}
		}
		if (k == -1)
			cout << 0 << '\n';
		else
			cout << sum(i, k) << '\n';
	}
}

E
计算子树中结点标号的平方和,树上启发式合并
将信息保存到重儿子中,复杂度O(nlogn)O(nlogn)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
vector<int> e[maxn];
int fa[maxn], sz[maxn], son[maxn];
ll ans[maxn];
//重链剖分
void dfs(int u) {
	sz[u] = 1;
	for (int v : e[u]) {
		fa[v] = u;
		dfs(v);
		sz[u] += sz[v];
		if (!son[u] || sz[son[u]] < sz[v])
			son[u] = v;
	}
}
set<ll> s;
ll res;
int Son;
//计算答案
void calc(int u) {
	s.insert(u);
	auto it = s.find(u);
	if (s.size() == 1) {

	}
	else if (it == s.begin()) {
		ll x = *(++s.begin()) - u;
		res += x * x;
	}
	else if (it == (--s.end())) {
		ll x = (*(--it)) - u;
		res += x * x;
	}
	else {
		auto itt = it;
		ll a = *(--it), b = u, c = *(++itt);
		res -= (a - c) * (a - c);
		res += (a - b) * (a - b) + (b - c) * (b - c);
	}

	for (int v : e[u]) {
		if (v == Son)
			continue;
		calc(v);
	}
}
//计算子树信息
void solve(int u, int keep) {
	for (int v : e[u]) {
		if (v == son[u])
			continue;
		solve(v, false);
	}

	if (son[u]) {
		solve(son[u], true);
		Son = son[u];
	}
	calc(u);
	Son = 0;
	ans[u] = res;
	if (!keep) {
		s.clear();
		res = 0;
	}
}
int main()
{
	ios::sync_with_stdio(false); cin.tie(0);

	int n; cin >> n;
	for (int i = 2; i <= n; ++i) {
		int x; cin >> x;
		e[x].push_back(i);
	}
	dfs(1);
	solve(1, true);
	for (int i = 1; i <= n; ++i)
		cout << ans[i] << '\n';
}

K
ac自动机的trie树上跑最短路

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 5;
const ll inf = 1e18;
struct Aho
{
	int ch[maxn][26], fail[maxn], val[maxn], cnt[maxn], len[maxn], tot = 0;
	void insert(char* s, int value) {
		int u = 0;
		for (int i = 0; s[i]; ++i) {
			int id = s[i] - 'a';
			if (!ch[u][id])
				ch[u][id] = ++tot;
			u = ch[u][id];
			len[u] = i + 1;
		}
		val[u] = val[u] == 0 ? value : min(val[u], value);
		cnt[u]++;
	}
	void build() {
		queue<int> q;
		for (int i = 0; i < 26; ++i) {
			if (ch[0][i]) {
				fail[ch[0][i]] = 0;
				q.push(ch[0][i]);
			}
		}

		while (!q.empty()) {
			int u = q.front(); q.pop();
			for (int i = 0; i < 26; ++i) {
				if (ch[u][i]) {
					fail[ch[u][i]] = ch[fail[u]][i];
					q.push(ch[u][i]);
				}
				else
					ch[u][i] = ch[fail[u]][i];
			}
		}
	}
	int query(char* s) {
		int now = 0, ans = 0;
		for (int i = 0; s[i]; ++i) {
			now = ch[now][s[i] - 'a'];
			for (int j = now; j && cnt[j] != -1; j = fail[j]) {
				ans += cnt[j];
				cnt[j] = -1;
			}
		}
		return ans;
	}
} aho;

char s[maxn];
ll dp[maxn];
int main()
{
	int n, m; scanf("%d", &n);
	for (int i = 0; i < n; ++i) {
		int x; scanf("%s%d", s, &x);
		aho.insert(s, x);
	}
	aho.build();
	scanf("%s", s + 1); m = strlen(s + 1);
	for (int i = 1; i <= m; ++i)
		dp[i] = inf;
	for (int i = 1, now = 0; i <= m; ++i) {
		now = aho.ch[now][s[i] - 'a'];
		int u = now;
		while (u) {
			if (aho.cnt[u])
				dp[i] = min(dp[i], dp[i - aho.len[u]] + aho.val[u]);
			u = aho.fail[u];
		}
	}
	if (dp[m] >= inf)
		printf("-1\n");
	else
		printf("%lld\n", dp[m]);
}

H
要求出现1-2,1-3,…,1-n,2-3,2-4,…,2-n,…,n-1-n的
即欧拉路径(要求走满所有路径);因为需要满足度数为奇数的点只能是0或2,不满足条件为偶数的情况,所以我们需要保留两个点,将其他点互相连接
求最大情况,我们二分m,需要的n,基础是完全图m*(m-1)/2条边,若为偶数m,加上m-2互相连接的(m-2)/2条边,最后再加上1转换为点数

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e6 + 5;
const int maxm = maxn << 1;
ll cal(ll m) {
	if (m == 2)
		return 2;
	ll rhs = m * (m - 1) / 2;
	if (m % 2 == 0)
		rhs += m / 2;
	else
		rhs++;
	return rhs;
}
ll solve(ll n) {
	ll st = 1, ed = 2e9;
	while (st != ed) {
		ll md = (st + ed + 1) >> 1;
		if (cal(md) <= n)
			st = md;
		else
			ed = md - 1;
	}
	return st;
}
vector<int> pr;
int head[maxn], tot = 0;
struct enode {
	int to, nxt;
} e[maxm];
void addedge(int u, int v) {
	e[tot] = {v, head[u]};
	head[u] = tot++;
}
bool used[maxm];
stack<int> stk;
void euler(int u) {
	stk.push(u);
	while (stk.size()) {
		int u = stk.top();
		int i = head[u];
		while (i != -1 && used[i])
			i = e[i].nxt;
		if (i != -1) {
			stk.push(e[i].to);
			used[i] = used[i ^ 1] = true;
			head[u] = e[i].nxt;
		}
		else {
			stk.pop();
			pr.push_back(u);
		}
	}
}

int main()
{
	ll n; scanf("%lld", &n);
	ll m; printf("%lld\n", m = solve(n));
	if (n > 2e6)
		return 0;

	memset(head, -1, sizeof(head));
	for (int i = 1; i <= m; ++i) {
		for (int j = i + 1; j <= m; ++j) {
			addedge(i, j);
			addedge(j, i);
		}
	}
	if (m % 2 == 0) {
		for (int i = 3; i <= m; i += 2) {
			addedge(i, i + 1);
			addedge(i + 1, i);
		}
	}
	euler(1);
	while (pr.size() < n)
		pr.push_back(1);
	for (int i = 0; i < pr.size(); ++i) {
		if (i == 0)
			printf("%d", pr[i]);
		else
			printf(" %d", pr[i]);
	}
	printf("\n");
}

F
我们考虑每次询问遍历s连接的所有边,乘上连接的子树中的点数
树链剖分+线段树维护

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;

vector<pair<int, int>>e[maxn];
int fa[maxn], sz[maxn], son[maxn], dep[maxn];
int top[maxn], id[maxn], rk[maxn];
int tot;
void dfs1(int u, int father) {
	dep[u] = dep[father] + 1; fa[u] = father; sz[u] = 1;
	for (auto &i : e[u]) {
		int v = i.first; 
		if (v == father) continue;
		dfs1(v, u);
		sz[u] += sz[v];
		if (sz[son[u]] < sz[v])
			son[u] = v;
	}
}
void dfs2(int u, int root) {
	top[u] = root; id[u] = ++tot; rk[tot] = u;
	if (son[u])
		dfs2(son[u], u);
	for (auto i : e[u]) {
		int v = i.first;
		if (v == fa[u] || v == son[u])
			continue;
		dfs2(v, u);
	}
}

ll t[maxn << 2];
void pushup(int rt) {
	t[rt] = t[rt << 1] + t[rt << 1 | 1];
}
void update(int rt, int l, int r, int pos, int val) {
	if (l == r) {
		t[rt] += val;
		return;
	}
	int mid = (l + r) >> 1;
	if (pos <= mid)
		update(rt << 1, l, mid, pos, val);
	else
		update(rt << 1 | 1, mid + 1, r, pos, val);
	pushup(rt);
}
ll query(int rt, int l, int r, int L, int R) {
	if (L <= l && r <= R)
		return t[rt];
	int mid = (l + r) >> 1;
	ll rhs = 0;
	if (L <= mid)
		rhs += query(rt << 1, l, mid, L, R);
	if (mid < R)
		rhs += query(rt << 1 | 1, mid + 1, r, L, R);
	return rhs;
}
int main()
{
	int n; scanf("%d", &n);
	int s = 1;
	for (int i = 1; i < n; ++i) {
		int u, v, w; scanf("%d%d%d", &u, &v, &w);
		e[u].push_back(make_pair(v, w));
		e[v].push_back(make_pair(u, w));
	}
	dfs1(1, 0);
	dfs2(1, 0);
	int m; scanf("%d", &m);
	while (m--) {
		int op, v, x; scanf("%d%d", &op, &v);
		if (op == 1) {
			scanf("%d", &x);
			update(1, 1, n, id[v], x);
		}
		else s = v;
		ll rhs = 0;
		for (auto i : e[s]) {
			int v = i.first, w = i.second;
			if (v == fa[s])
				rhs += 1ll * w * (t[1] - query(1, 1, n, id[s], id[s] + sz[s] - 1));
			else
				rhs += 1ll * w * query(1, 1, n, id[v], id[v] + sz[v] - 1);
		}
		printf("%lld\n", rhs);
	}
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!