省選模擬 19/09/11

ps. 博主趁信息課摸魚考的暴零模擬
看門人
憑感覺就知道是長鏈剖分,將路徑查分一下,disu+disv2dislcadis_u+dis_v-2*dis_{lca}
維護fu,if_{u,i}表示u的子樹,深度爲 i 的點的 disdis最大值
考慮如何合併長鏈
枚舉一條長鏈,在另一邊的長鏈上對應一段區間,可以用線段樹查詢一下區間的最大disdis
然後合併的時候需支持單點取 maxmax ,類似重鏈,長鏈剖分也可以有dfsdfs序,就是先 dfsdfs 長兒子就可以了

#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;
}                    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章