更好的阅读体验
Maex
题意
给定一棵树,你需要给树上的每个点赋一个权值 a i a_i ai,要求不存在两个点 i , j i,j i,j存在 a i = a j a_i=a_j ai=aj,每个点的 b i = M E X ( s u n t r e e ( a i ) ) b_i=MEX(suntree(a_i)) bi=MEX(suntree(ai)),求 ∑ i = 1 n b i \sum_{i=1}^{n}b_i ∑i=1nbi。
思路
通过观察发现 答案为每点的子树大小加上子节点中能凑出的最大答案。
int n, son[N];
vector<int> G[N];
ll sz[N], mx[N];
void dfs(int x, int fa) {
sz[x] = 1;
ll res = 0;
for (int it : G[x]) {
if (it == fa)continue;
dfs(it, x);
res = max(res, mx[it]);
sz[x] += sz[it];
}
mx[x] = res + sz[x];
}
int main() {
int T;
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
G[x].push_back(y);
G[y].push_back(x);
}
dfs(1, 0);
cout << mx[1] << endl;
for (int i = 1; i <= n; i++) G[i].clear(), sz[i] = mx[i] = 0;
}
return 0;
}
Shinobu loves trip
题意
给定 p , a , n , q p,a,n,q p,a,n,q,接下来 n n n次操作,每次给定 s i , d i s_i,d_i si,di表示从 s i s_i si开始,每次走到 s i ∗ a k % P 0 ≤ k ≤ d i s_i*a^k \%P \ \ 0\le k\le d_i si∗ak%P 0≤k≤di,的位置,之后 q q q次询问,每次询问给出一个 x x x,输出 x x x这个位置被多少对 s i , d i s_i,d_i si,di走过。
思路
对于每个 s i , d i s_i,d_i si,di,我们都能判断是否存在一个 k k k使得$s_i*a^k =x\ \ k \le d_i $即可。
ll s[N], d[N], ans[N], a[N], invs[N];
int main() {
int T;
cin >> T;
while (T--) {
ll p, A, n, q;
cin >> p >> A >> n >> q;
gp_hash_table<int, int> mp;
for (int i = 1; i <= n; i++) cin >> s[i] >> d[i], invs[i] = qpow(s[i], p - 2, p);
a[0] = 1;
for (int i = 1; i <= 200000; i++) a[i] = a[i - 1] * A % p;
for (int i = 200000; i >= 0; i--) mp[a[i]] = i;
for (int i = 1; i <= q; i++) {
ll x;
cin >> x;
int ans = 0;
for (int j = 1; j <= n; j++) {
if (s[j] == 0) {
ans += (x == 0);
continue;
}
ll tmp = x * invs[j] % p;
auto it = mp.find(tmp);
if (mp.find(tmp) != mp.end() && it->second <= d[j]) ans++;
}
cout << ans << endl;
}
}
return 0;
}
Shinobu Loves Segment Tree
题意
给定一个 n n n和 x x x,假设 d e g t r e e i ( t r [ x ] ) degtree_i(tr[x]) degtreei(tr[x])表示区间长度为 i i i建立的线段树中第 x x x号节点的区间长度,求 ∑ i = 1 n d e g t r e e i ( t r [ x ] ) \sum_{i=1}^{n} degtree_i(tr[x]) ∑i=1ndegtreei(tr[x])
思路
通过打表观察我们发现,对于一个
x
x
x,当区间长度小于一定值的时候答案为
0
0
0,我们可以以
l
o
g
log
log的复杂度算出这个长度,然后后面的数字便有以下规律:
- 若 x x x为奇数,则对于所有大于 0 0 0的长度答案分别为 2 k 2^k 2k个 1 1 1, 2 k 2^k 2k个 2 2 2, 2 k 2^k 2k个 3 3 3, 2 k 2^k 2k个 3 3 3…
- 若 x x x为偶数,则对于所有大于 0 0 0的长度答案分别为 2 k − 2^{k-} 2k−个 1 1 1, 2 k 2^k 2k个 2 2 2, 2 k 2^k 2k个 3 3 3, 2 k 2^k 2k个 3 3 3…
以上 k = l o g ( x ) − 1 k=log(x)-1 k=log(x)−1
int n;
ll x;
ll get(vector<int> now) {
ll res = 1;
for (int it : now) {
if (it == 1 || res == 1) res += res;
else res += res - 1;
}
return res;
}
int main() {
int T;
cin >> T;
while (T--) {
ll n, x;
cin >> n >> x;
if (x == 1) {
ll res = (n + 1) * n / 2;
cout << res << endl;
continue;
}
vector<int> now;
ll tmp = x;
while (tmp) {
now.push_back(tmp % 2);
tmp /= 2;
}
now.pop_back();
ll tot = get(now);
tmp = 1ll << now.size();
// cerr<<tmp<<' '<<tot<<endl;
ll ans = 0;
ll p = 2;
if (tot > n) {
cout << 0 << endl;
goto h;
}
n -= tot - 1;
if (x % 2 == 0) {
if (n < tmp / 2) { cout << n << endl; goto h; }
else n -= tmp / 2, ans = tmp / 2;
}
else {
if (n < tmp) { cout << n << endl; goto h; }
else n -= tmp, ans = tmp;
}
while (n >= tmp) {
ans += tmp * p;
n -= tmp;
p++;
}
ans += n * p;
cout << ans << endl;
h:;
}
return 0;
}
Planar Graph
题意
给定一个平面图,问最少需要在哪几条边上开口,使得所有的面都连通,输出字典序最小的方案
思路
将面看作点,问题便可以装化成生成树问题,跑一次最大生成树,不在树上的边都是要删掉的边。此处图可能不连通,所以应该算是最大生成森林。
struct node {
int l, r, w;
}a[N];
int f[N], m, n;
int find(int x) {
return f[x] == x ? x : f[x] = find(f[x]);
}
int main() {
int T;
cin >> T;
while (T--) {
cin >> n >> m;
for (int i = 1; i <= n; i++) f[i] = i;
for (int i = 1; i <= m; i++) cin >> a[i].l >> a[i].r, a[i].w = i;
sort(a + 1, a + 1 + m, [](node A, node B) {
return A.w > B.w;
});
vector<int> ans;
for (int i = 1; i <= m; i++) {
int f1 = find(a[i].l), f2 = find(a[i].r);
if (f1 == f2) ans.push_back(a[i].w);
else {
f[f1] = f2;
}
}
cout << ans.size() << endl;
sort(ans.begin(), ans.end());
for (int it : ans) cout << it << ' ';
cout << endl;
}
return 0;
}
Loop
题意
给定一个序列,每次可以选择一个数字将其删除,并在之后的某个位置将其插进去,求 k k k次操作之后字典序最大的答案。
思路
从第一个位置开始之后的 k k k个 a i < a i + 1 a_i<a_{i+1} ai<ai+1的情况都是需要向后移动的,从前向后遍历的过程中,用一个优先队列存已经删掉的数字,再次遍历删除过这些数字后的序列, 若遇到某个位置 a i < q . t o p ( ) a_i<q.top() ai<q.top(),就将该数字插在这里。
int n, k, a[N];
int main() {
int T;
cin >> T;
while (T--) {
cin >> n >> k;
for (int i = 1; i <= n; i++) cin >> a[i];
vector<int>ans;
priority_queue<int> q;
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (!ans.size()) {
ans.push_back(a[i]);
continue;
}
while (ans.size() && a[i] > ans.back() && cnt < k) {
cnt++;
q.push(ans.back());
ans.pop_back();
}
ans.push_back(a[i]);
}
vector<int> res;
for (int it : ans) {
while (q.size() && it < q.top()) res.push_back(q.top()), q.pop();
res.push_back(it);
}
while (q.size()) res.push_back(q.top()), q.pop();
cout << res[0];
for (int i = 1; i < res.size(); i++) cout << ' ' << res[i];
cout << endl;
}
return 0;
}