ps. 博主趁信息課摸魚考的暴零模擬
看門人
憑感覺就知道是長鏈剖分,將路徑查分一下,
維護表示u的子樹,深度爲 i 的點的 最大值
考慮如何合併長鏈
枚舉一條長鏈,在另一邊的長鏈上對應一段區間,可以用線段樹查詢一下區間的最大
然後合併的時候需支持單點取 ,類似重鏈,長鏈剖分也可以有序,就是先 長兒子就可以了
#include<bits/stdc++.h>
#define N 1000050
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = (cnt << 1) + (cnt << 3) + (ch-'0'), ch = getchar();
return cnt * f;
}
typedef long long ll;
const int Mod = 998244353;
ll add(ll a, ll b){ return (a + b) % Mod;}
ll mul(ll a, ll b){ return (a * b) % Mod;}
int first[N], nxt[N], to[N], w[N], tot;
void adde(int x, int y, int z){
nxt[++tot] = first[x], first[x] = tot, to[tot] = y, w[tot] = z;
}
int n, L[N], R[N];
int len[N], son[N], in[N], pos[N], sign;
ll ans[N], dis[N];
void dfs(int u){
int typ = 0;
for(int i = first[u]; i; i = nxt[i]){
int t = to[i]; typ = 1; dis[t] = dis[u] + (ll)w[i];
dfs(t); if(len[t] >= len[son[u]]) son[u] = t;
} if(typ) len[u] = len[son[u]] + 1;
}
void dfs2(int u){
in[u] = ++sign; pos[sign] = u; if(son[u]) dfs2(son[u]);
for(int i = first[u]; i; i = nxt[i]) if(to[i] ^ son[u]) dfs2(to[i]);
}
ll mx[N<<2];
void pushup(int x){ mx[x] = max(mx[x<<1], mx[x<<1|1]);}
void build(int x, int l, int r){
if(l == r){ mx[x] = dis[pos[l]]; return;}
int mid = (l+r) >> 1;
build(x<<1, l, mid); build(x<<1|1, mid+1, r);
pushup(x);
}
void upt(int x, int l, int r, int pos, ll v){
mx[x] = max(mx[x], v);
if(l == r) return; int mid = (l+r) >> 1;
if(pos <= mid) upt(x<<1, l, mid, pos, v);
else upt(x<<1|1, mid+1, r, pos, v);
}
ll query(int x, int l, int r, int L, int R){
if(L<=l && r<=R) return mx[x];
int mid = (l+r) >> 1; ll ans = 0;
if(L<=mid) ans = max(ans, query(x<<1, l, mid, L, R));
if(R>mid) ans = max(ans, query(x<<1|1, mid+1, r, L, R));
return ans;
}
void dfs3(int u){
ans[u] = -1; if(!son[u]) return; dfs3(son[u]);
if(L[u] <= len[u]) ans[u] = query(1, 1, n, in[u] + L[u], in[u] + min(R[u], len[u])) - dis[u];
for(int i = first[u]; i; i = nxt[i]){
int t = to[i]; if(t == son[u]) continue;
dfs3(t);
for(int i = 0; i <= len[t]; i++){
if(L[u] <= i+1 && i+1 <= R[u]) ans[u] = max(ans[u], query(1, 1, n, in[t] + i, in[t] + i) - dis[u]);
int l = max(1, L[u] - i - 1);
int r = min(len[u], R[u] - i - 1);
if(l > r) continue;
ans[u] = max(ans[u], query(1, 1, n, in[t] + i, in[t] + i) + query(1, 1, n, in[u] + l, in[u] + r) - dis[u] * 2);
}
for(int i = 1; i <= len[t]+1; i++) upt(1, 1, n, in[u] + i, query(1, 1, n, in[t] + i - 1, in[t] + i - 1));
}
}
int main(){
n = read();
for(int i = 1; i <= n; i++) L[i] = read(), R[i] = read();
for(int i = 2; i <= n; i++){ int x = read(), c = read(); adde(x, i, c);}
dfs(1); dfs2(1); build(1, 1, n); dfs3(1);
ll tmp = 1, sum = 0;
for(int i = n; i >= 1; i--){
ll res = ans[i] == -1 ? (Mod - 1) : (ans[i] % Mod);
sum = add(sum, mul(tmp, res)); tmp = mul(tmp, 23333);
} cout << sum; return 0;
}
仙人球
如果是樹的話就是一個簡單的揹包…
我們可以把環單獨提出來,定一個環的根,把一個環拆成一條鏈出來
然後分別計算不經過環根的與經過環根的
經過環根的一定是兩邊拼起來,強制一邊選到一個位置,另一邊就是一個前綴和
#include<bits/stdc++.h>
#define N 5050
#define M 105
#define pb push_back
using namespace std;
const int Mod = 1e9 + 7;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = (cnt << 1) + (cnt << 3) + (ch-'0'), ch = getchar();
return cnt * f;
}
int add(int a, int b){ return (a + b) >= Mod ? a + b - Mod : a + b;}
int mul(int a, int b){ return 1ll * a * b % Mod;}
void Add(int &a, int b){ a = add(a, b);}
vector<int> e[N];
int f[N][M], pre[N][M], suf[N][M];
int dfn[N], sign, bel[N], cnt, isrt[N];
int n, m, k, from[N], vis[N], ans;
vector<int> v[N];
void tarjan(int u, int fa){
dfn[u] = ++sign;
for(int i = 0; i < e[u].size(); i++){
int t = e[u][i]; if(t == fa) continue;
if(!dfn[t]) from[t] = u, tarjan(t, u);
else{
if(dfn[t] < dfn[u]){
cnt++; isrt[t] = 1; bel[t] = cnt;
for(int x = u; x != t; x = from[x]) bel[x] = cnt, v[cnt].pb(x);
}
}
}
}
int tmp[M], tmp2[M];
void dp(int *A, int *B, int *C){
for(int x = k; x; x--)
for(int y = 1; y < x; y++) Add(A[x], mul(B[y], C[x-y]));
}
void calc(int u){
vector<int> now = v[bel[u]];
int n = now.size() - 1;
memcpy(tmp, f[now[0]], sizeof(tmp));
memset(pre, 0, sizeof(pre));
memset(suf, 0, sizeof(suf));
for(int i = 1; i <= n; i++){
memset(tmp2, 0, sizeof(tmp2));
dp(tmp2, tmp, f[now[i]]);
memcpy(tmp, tmp2, sizeof(tmp));
for(int x = 2; x <= k; x++) Add(ans, tmp[x]);
for(int x = 1; x <= k; x++) Add(tmp[x], f[now[i]][x]);
}
memcpy(pre[0], f[now[0]], sizeof(pre[0]));
for(int i = 1; i <= n; i++){
dp(pre[i], pre[i-1], f[now[i]]);
}
memcpy(suf[n], f[now[n]], sizeof(suf[n]));
for(int i = n-1; i >= 0; i--){
dp(suf[i], suf[i+1], f[now[i]]);
}
memset(tmp, 0, sizeof(tmp));
for(int i = 0; i <= n; i++)
for(int j = 1; j <= k; j++)
Add(tmp[j], pre[i][j]);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= k; j++)
Add(tmp[j], suf[i][j]);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= k; j++)
Add(pre[i][j], pre[i-1][j]);
for(int i = 0; i <= n-2; i++)
dp(tmp, pre[i], suf[i+2]);
dp(f[u], f[u], tmp);
}
void dfs(int u){
vis[u] = 1; f[u][1] = 1;
for(int i = 0; i < e[u].size(); i++){
int t = e[u][i]; if(vis[t]) continue;
dfs(t);
if(!bel[u] || bel[u] != bel[t]){
dp(f[u], f[u], f[t]);
}
} if(isrt[u]) calc(u);
for(int i = 1; i <= k; i++) Add(ans, f[u][i]);
}
int main(){
n = read(), m = read(), k = read();
for(int i = 1; i <= m; i++){
int x = read(), y = read();
e[x].pb(y); e[y].pb(x);
} tarjan(1, 0);
dfs(1); cout << ans; return 0;
}