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