A层验题1
模拟退役的天数++
A. 数数
对于这种题,直接猜结论,
上来啥都不想,直接拍个序
如果 \(k = 2\) 显然选择最大和最小
猜测 \(k - > k + 1\) 时原来选的不会改变
感觉好像是对的,那么继续猜测在剩下的元素中选择最小或最大的一定最优
写出来不知道对不对怎么办?
写暴力 + 对拍
证明?狗都不证
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){
int x = 0; bool f = 0; char c = getchar();
while(c < '0' || c > '9'){if(c == '-')f = 1; c = getchar();}
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
return f ? -x : x;
}
const int maxn = 300005;
int n, a[maxn];
int main(){
n = read();
for(int i = 1; i <= n; ++i)a[i] = read();
sort(a + 1, a + n + 1);
int pl = 1, pr = n;
ll cl = 0, cr = 0, sl = 0, sr = 0, ans = 0;
for(int k = 1; k <= n; ++k){
ll nl = a[pl] * cl - a[pl] * cr;
ll nr = a[pr] * cl - a[pr] * cr;
if(nl > nr){
ans += nl - sl + sr;
sl += a[pl]; ++cl; ++pl;
}else{
ans += nr - sl + sr;
sr += a[pr]; ++cr; --pr;
}
printf("%lld\n", ans);
}
return 0;
}
B. 数树
离谱的 \(dp\) + 容斥
用类似树形背包求出至少有 \(i\) 条边不合法的方案数 \(g_i\)
然后 \(ans = g_0 - g_1 + g_2 - ......\)
如何求 \(g\)
设 \(f_{i, j, 0 / 1 / 2 / 3}\)
表示在子树 \(i\) 中选择了 \(j\) 条边不合法,根节点 \(j\)
\(0\) 没有入边和出边不合法
\(1\) 有且仅有一条出边不合法
\(2\) 有且仅有一条入边不合法
\(3\) 尤且仅有一条入边和一条出边不合法
然后就是 \(dfs\) 时候跑树形背包,然后枚举当前点到他儿子的边选或不选
注意上面有且仅有是因为一个点出边/入边只能有一个不合法,原因显然
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
return x;
}
const int maxn = 5005;
const int mod = 998244353;
int n;
void add(int &x, int y){x += y; x = x >= mod ? x - mod : x;}
struct edge{int to, net, op;}e[maxn << 1 | 1];
int head[maxn], tot;
void add(int u, int v, int op){
e[++tot].net = head[u];
head[u] = tot;
e[tot].to = v;
e[tot].op = op;
}
int fac[maxn], f[maxn][maxn][4], tmp[maxn][4], size[maxn];
void dfs(int x, int fa){
f[x][0][0] = 1;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v == fa)continue;
dfs(v, x);
for(int j = 0; j <= n; ++j)
for(int k = 0; k <= 3; ++k)
tmp[j][k] = 0;
for(int j = 0; j <= size[x]; ++j)
for(int k = 0; k <= size[v]; ++k){
int sum = 0;
for(int p = 0; p < 4; ++p)add(sum, f[v][k][p]);
for(int p = 0; p < 4; ++p)add(tmp[j + k][p], 1ll * sum * f[x][j][p] % mod);
if(e[i].op){
int ls = (f[v][k][0] + f[v][k][2]) % mod;
add(tmp[j + k + 1][2], 1ll * f[x][j][0] * ls % mod);
add(tmp[j + k + 1][3], 1ll * f[x][j][1] * ls % mod);
}else{
int ls = (f[v][k][0] + f[v][k][1]) % mod;
add(tmp[j + k + 1][1], 1ll * f[x][j][0] * ls % mod);
add(tmp[j + k + 1][3], 1ll * f[x][j][2] * ls % mod);
}
}
size[x] += size[v];
for(int j = 0; j <= size[x]; ++j)
for(int k = 0; k < 4; ++k)
f[x][j][k] = tmp[j][k];
}
++size[x];
}
int main(){
n = read();
fac[0] = 1; for(int i = 1; i <= n; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
for(int i = 1; i < n; ++i){
int u = read(), v = read();
add(u, v, 1); add(v, u, 0);
}
dfs(1, 0);
int opt = 1, ans = 0;
for(int i = 0; i < n; ++i){
int sum = 0; for(int j = 0; j < 4; ++j)add(sum, f[1][i][j]);
add(ans, (1ll * opt * fac[n - i] % mod * sum % mod + mod ) % mod);
opt = -opt;
}
printf("%d\n",ans);
return 0;
}
C. 鼠树
数据结垢
白点的权值只会随着黑点改变,而且在很大成都上与黑点保持一致
那么考虑维护每个黑点的管辖点数量,以及黑节点的权值,为了子树求和再维护他们的乘积
查询白点就能直接找黑点权值
这个时候如果我们添加一个黑节点,就需要查询他的子树内黑节点管辖点数量和,用子树大小减去即可得到他的管辖点数量,他的权值设为他原来的归属点的权值, 当然也需要修改他原来的归属点
这时我们就需要快速求出一个点的归属点了,考虑进行树剖, 对每条链维护一个 \(set\) , 维护当前链内黑点,那么每次查询只需要向上跳,然后在 \(set\) 里二分即可
注意这里比较细节, \(set\) 里只能维护 \(dfs\)序,这样才能二分, \(set\) 里有元素不一定就找到了答案,他们可能在另外一条链的下面,所以往上跳后查询新点的 \(dfs\)序之前先看看当前 \(set\) 里是否存在小于他的
最后的问题是删除,对黑点的影响可以类比加入搞一下,问题在于他的管辖点权值会变,那么假设他新的归属点权值为\(x\) ,他的权值为 \(y\), 那么新开一棵线段树维护差值,给他整棵子树加上\(x - y\),再用 \(4\) 操作消除对非管辖点的贡献即可
码农题,,,,
code
#include<bits/stdc++.h>
using namespace std;
typedef unsigned int uint;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
return x;
}
const int maxn = 300005;
int n, m;
int head[maxn], tot;
struct edge{int net, to;}e[maxn << 1 | 1];
void add(int u, int v){
e[++tot].net = head[u];
head[u] = tot;
e[tot].to = v;
}
struct delet{
struct node{
uint val, tag;
}t[maxn << 2 | 1];
void upd(int x, int l, int r, uint val){
t[x].val += (r - l + 1) * val;
t[x].tag += val;
}
void push_down(int x, int l, int r){
int mid = (l + r ) >> 1;
upd(x << 1, l, mid, t[x].tag);
upd(x << 1 | 1, mid + 1, r, t[x].tag);
t[x].tag = 0;
}
void push_up(int x){t[x].val = t[x << 1].val + t[x << 1 | 1].val;}
void modify(int x, int l, int r, int L, int R, int val){
if(L <= l && r <= R){
upd(x, l, r, val);
return;
}
if(t[x].tag)push_down(x, l, r);
int mid = (l + r) >> 1;
if(L <= mid)modify(x << 1, l, mid, L, R, val);
if(R > mid)modify(x << 1 | 1, mid + 1, r, L, R, val);
push_up(x);
}
uint query(int x, int l, int r, int L, int R){
if(L <= l && r <= R)return t[x].val;
int mid = (l + r) >> 1; uint ans = 0;
if(t[x].tag)push_down(x, l, r);
if(L <= mid)ans += query(x << 1, l, mid, L, R);
if(R > mid)ans += query(x << 1 | 1, mid + 1, r, L, R);
return ans;
}
}D;
struct BLACK{
struct node{
uint val, mul, c, tag;
}t[maxn << 2 | 1];
void upd(int x, int val){
t[x].val += val;
t[x].mul += val * t[x].c;
t[x].tag += val;
}
void push_down(int x){
if(t[x << 1].c)upd(x << 1, t[x].tag);
if(t[x << 1 | 1].c)upd(x << 1 | 1, t[x].tag);
t[x].tag = 0;
}
void push_up(int x){
t[x].mul = t[x << 1].mul + t[x << 1 | 1].mul;
t[x].c = t[x << 1].c + t[x << 1 | 1].c;
}
void modify(int x, int l, int r, int L, int R, int val){
if(L <= l && r <= R){
if(t[x].c)upd(x, val);
return;
}
if(t[x].tag)push_down(x);
int mid = (l + r) >> 1;
if(L <= mid)modify(x << 1, l, mid, L, R, val);
if(R > mid)modify(x << 1 | 1, mid + 1, r, L, R, val);
push_up(x);
}
void modify_one(int x, int l, int r, int pos, int c, int val){
if(l == r){
t[x].c = c; t[x].val = val;
t[x].mul = t[x].c * t[x].val;
return;
}
if(t[x].tag)push_down(x);
int mid = (l + r) >> 1;
if(pos <= mid) modify_one(x << 1, l, mid, pos, c, val);
else modify_one(x << 1 | 1, mid + 1, r, pos, c, val);
push_up(x);
}
int query_c(int x, int l, int r, int L, int R){
if(L <= l && r <= R)return t[x].c;
if(t[x].tag)push_down(x);
int mid = (l + r) >> 1; int ans = 0;
if(L <= mid)ans += query_c(x << 1, l, mid, L, R);
if(R > mid)ans += query_c(x << 1 | 1, mid + 1, r, L, R);
return ans;
}
uint query_mul(int x, int l, int r, int L, int R){
if(L <= l && r <= R)return t[x].mul;
if(t[x].tag)push_down(x);
int mid = (l + r) >> 1; uint ans = 0;
if(L <= mid)ans += query_mul(x << 1, l, mid, L, R);
if(R > mid)ans += query_mul(x << 1 | 1, mid + 1, r, L, R);
return ans;
}
uint query_val(int x, int l, int r, int pos){
if(l == r)return t[x].val;
if(t[x].tag)push_down(x);
int mid = (l + r) >> 1;
if(pos <= mid)return query_val(x << 1, l, mid, pos);
else return query_val(x << 1 | 1, mid + 1, r, pos);
}
}B;
set<int>black[maxn];
int son[maxn], size[maxn], top[maxn], id[maxn], fa[maxn], dep[maxn], idr[maxn], nid[maxn];
void dfs1(int x){
size[x] = 1;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
dep[v] = dep[x] + 1;
dfs1(v);
size[x] += size[v];
son[x] = size[son[x]] > size[v] ? son[x] : v;
}
}
int tim;
void dfs2(int x, int tp){
id[x] = ++tim; nid[tim] = x; top[x] = tp;
if(son[x])dfs2(son[x], tp);
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v == son[x])continue;
dfs2(v, v);
}
idr[x] = tim;
}
int get_black_fa(int x){
while(black[top[x]].empty() || (*black[top[x]].begin()) > id[x])
x = fa[top[x]];
return nid[*(--black[top[x]].upper_bound(id[x]))];
}
int main(){
n = read(), m = read();
for(int i = 2; i <= n; ++i) add(fa[i] = read(), i);
dfs1(1); dfs2(1, 1);
black[1].insert(1); B.modify_one(1, 1, n, 1, size[1], 0);
for(int i = 1; i <= m; ++i){
uint op = read();
if(op == 1){
int x = read(); int bl = get_black_fa(x);
cout << B.query_val(1, 1, n, id[bl]) + D.query(1, 1, n, id[x], id[x]) << endl;
}
if(op == 2){
int k = read(), w = read();
B.modify(1, 1, n, id[k], id[k], w);
}
if(op == 3){
int x = read();
uint ans = B.query_mul(1, 1, n, id[x], idr[x]);
uint bl = get_black_fa(x), vbl = B.query_val(1, 1, n, id[bl]);
uint res = size[x] - B.query_c(1, 1, n, id[x], idr[x]);
ans += res * vbl;
ans += D.query(1, 1, n, id[x], idr[x]);
cout << ans << '\n';
}
if(op == 4){
int k = read(), w = read();
B.modify(1, 1, n, id[k], idr[k], w);
}
if(op == 5){
int x = read(); int bl = get_black_fa(x);
int val = B.query_val(1, 1, n, id[bl]);
int nc = size[x] - B.query_c(1, 1, n, id[x], idr[x]);
B.modify_one(1, 1, n, id[x], nc, val);
B.modify_one(1, 1, n, id[bl], B.query_c(1, 1, n, id[bl], id[bl]) - nc, val);
black[top[x]].insert(id[x]);
}
if(op == 6){
int x = read();
uint vx = B.query_val(1, 1, n, id[x]), cx = B.query_c(1, 1, n, id[x], id[x]);
uint bl = get_black_fa(fa[x]), cb = B.query_c(1, 1, n, id[bl], id[bl]), vb = B.query_val(1, 1, n, id[bl]);
B.modify_one(1, 1, n, id[bl], cb + cx, B.query_val(1, 1, n, id[bl]));
D.modify(1, 1, n, id[x], idr[x], vx - vb);
B.modify_one(1, 1, n, id[x], 0, 0);
black[top[x]].erase(id[x]);
B.modify(1, 1, n, id[x], idr[x], vb - vx);
}
}
return 0;
}
D. ckw的树
离离原上谱,艹
发现有
\(E(x) = aE(fa(fa(x))) + bE(fa(x)) + cE(brother(x)) + dE(son(x)) + fE(son(son(x))) + g\)
考虑如何去掉一些
发现叶子节点没有后面的 \(d, f\) 项,考虑用 \(a, b, g\) 系数变化代替难求的 \(brother\) (注意 \(brother\) 包括该点本身)
对于该节点的父亲 \(y\) ,有
\(\sum E(son(y)) = \sum a_x E(fa(y)) + \sum b_x E(y) + \sum c_x son(y) + \sum g_x\)
有
\(E(brother(x)) = \sum E(son(y)) = \frac{ \sum a_x E(fa(y)) + \sum b_x E(y) + \sum g_x} {1 - c}\)
带入叶子上面没有 \(d, f\) 的项就可以写成
\(E(x) = a E(fa(fa(x))) + bE(fa(x)) + c E(x) + d\) 的形式(这里跟上面系数不同)
如果所有点都写成这种形式,对于根节点就能算出答案,然后带入求解即可
考虑如何消去一个非叶节点的 \(d, e\) 项
就是推一下关系,儿子的父亲是我自己,儿子的爷爷是我父亲这样,系数对应贡献即可
说的简单,但是我真的不是很理解,也只是褐的大佬的题解
简单说一下操作过程
先用已知方程消去 \(d, e\)项
在父亲处消去 \(c\) , 再作为已知方程对上面有所贡献
代码加了一点注释
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
return x;
}
const int maxn = 100005;
const int mod = 998244353;
void add(int &x, int y){x += y; x = x >= mod ? x - mod : x;}
int qpow(int x, int y){
int ans = 1;
for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1) ans = 1ll * ans * x % mod;
return ans;
}
int head[maxn], tot;
struct edge{
int to, net;
}e[maxn << 1 | 1];
bool flower = 1;
int n, m;
bool iskey[maxn];
void link(int u, int v){
e[++tot].net = head[u];
head[u] = tot;
e[tot].to = v;
}
struct arr{int a, b, c, d;}f[maxn], g[maxn];
int son[maxn], ans[maxn], cnt[maxn];
void dfs1(int x, int fa, int ff){
cnt[x] = son[fa] + (fa > 0) + (ff > 0);
son[x] = 0;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v == fa)continue;
++son[x];
}
int a = 0, b = 0, c = 1, d = 0;
// fa(fa(x)) fa(x) brother d
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v == fa)continue;
dfs1(v, x, fa);
cnt[x] += son[v] + 1;
add(a, f[v].a); add(b, f[v].b);
add(c, mod - f[v].c); add(d, f[v].d);
}
int invd = qpow(cnt[x], mod - 2);
f[x].a = f[x].b = f[x].c = invd;
f[x].d = 1;
int k = 1, invc = c ? qpow(c, mod - 2) : 0;
a = 1ll * a * invc % mod; b = 1ll * b * invc % mod; d = 1ll * d * invc % mod;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v == fa)continue;
add(f[v].a, 1ll * f[v].c * a % mod);
add(f[v].b, 1ll * f[v].c * b % mod);
add(f[v].d, 1ll * f[v].c * d % mod);
f[v].c = 0;
//消去儿子的c项
int mul = 1ll * (g[v].b + 1) * invd % mod;//孙子的b项+儿子,是儿子的贡献
add(f[x].b, 1ll * f[v].a * mul % mod);
add(f[x].d, 1ll * f[v].d * mul % mod);
add(k, mod - (1ll * f[v].b * mul % mod));
add(f[x].d, 1ll * g[v].d * invd % mod);//孙子的贡献,孙子自己的c项已经化为1,故不做特殊处理
add(k, mod - (1ll * g[v].a * invd % mod));
add(g[x].a, f[v].a); add(g[x].b, f[v].b); add(g[x].d, f[v].d);//记录儿子的贡献,在自己父亲那里当孙子用
}
if(iskey[x])f[x] = {0, 0, 0, 0};
else{
int invk = qpow(k, mod - 2);//系数化为c
f[x].a = 1ll * f[x].a * invk % mod;
f[x].b = 1ll * f[x].b * invk % mod;
f[x].c = 1ll * f[x].c * invk % mod;
f[x].d = 1ll * f[x].d * invk % mod;
}
}
void dfs2(int x, int fa, int ff){
ans[x] = (1ll * f[x].a * ans[ff] % mod + 1ll * f[x].b * ans[fa] % mod + f[x].d) % mod;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to; if(v == fa)continue;
dfs2(v, x, fa);
}
}
int main(){
n = read(), m = read();
for(int i = 1; i < n; ++i){
int u = read(), v = read();
if(u != 1 && v != 1)flower = 0;
link(u, v); link(v, u);
}
for(int i = 1; i <= m; ++i)iskey[read()] = true;
son[0] = 1;
dfs1(1, 0, 0);
int invc = qpow((mod + 1 - f[1].c) % mod, mod - 2);
f[1].d = 1ll * f[1].d * invc % mod;
dfs2(1, 0, 0);
for(int i = 1; i <= n; ++i)printf("%d\n",ans[i]);
return 0;
}