2023冲刺国赛模拟19

A. 矩阵

正解是二维分块

image

但是二维树状数组跑的飞快

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

ll read(){
	ll x = 0; bool f = false; char c = getchar();
	while(!isdigit(c))f = c == '-', c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return f ? -x : x;
}

const int maxn = 1005;
int n, m, q;
struct BIT{
	ll t[maxn][maxn];
	#define lowbit(x) (x & -x)
	void add(int x, int y, ll val){
		for(; x <= n; x += lowbit(x))
			for(int b = y; b <= m; b += lowbit(b))
				t[x][b] += val;
	}
	ll query(int x, int y){
		ll ans = 0;
		for(; x; x -= lowbit(x))
			for(int b = y; b; b -= lowbit(b))
				ans += t[x][b];
		return ans;
	}
}T;
int main(){
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);
	n = read(), m = read(), q = read();
	for(int i = 1; i <= q; ++i){
		int op = read(), a = read(), b = read();
		if(op & 1)T.add(a, b, read());
		else{
			int c = read(), d = read();
			printf("%lld\n",T.query(c, d) - T.query(c, b - 1) - T.query(a - 1, d) + T.query(a - 1, b - 1));
		}
	}
	return 0;
}

B. 种草

费用流建图 + Primal-Dual 原始对偶算法

建边 \((i, i + 1, a[i], 0)\) (特别的 \((n, t, a[n], 0)\)

\((s, i, a[i] - a[i - 1], 0)\)

对于 \((l, r, w)\) 建边 \((l, r + 1, 1, w)\)

跑最大费用最大流

Primal-Dual 原始对偶算法

就是给每个点一个势能,使得任意边权非负,然后就能用 \(dijstra\)

初始势能是源点到每个点的最短路,新边权就是 \(h[u] - h[v] + w[u][v]\)

每一轮增广后,势能加上本次的最短路,证明可以见 \(rval\) 学长的经典博客

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, int> pli;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const ll inf = 1e15;
const int maxn = 1e5 + 55;
int n, m, a[maxn];
struct MCMF{
	int s, t;
	int head[maxn], tot = 1;
	struct edge{int to, net; ll val, cost;}e[maxn * 4];
	void add(int u, int v, int w, int c){
		e[++tot].net = head[u];
		head[u] = tot;
		e[tot].to = v;
		e[tot].val = w;
		e[tot].cost = c;
	}
	void link(int u, int v, int w, int c){add(u, v, w, c); add(v, u, 0, -c);}
	ll h[maxn]; bool vis[maxn];
	void spfa(){
		h[s] = 0;
		for(int x = 1; x <= n; ++x)
			for(int i = head[x]; i; i = e[i].net){
				int v = e[i].to;
				if(e[i].val && h[v] > h[x] + e[i].cost)
					h[v] = h[x] + e[i].cost;
			}
	}
	ll dis[maxn]; int pre[maxn], pe[maxn];
	bool dij(){
		priority_queue<pli>q;
		for(int i = 1; i <= t; ++i)dis[i] = inf;
		for(int i = 1; i <= t; ++i)vis[i] = false;
		dis[s] = 0; q.push(pli(0, s));
		while(!q.empty()){
			int x = q.top().second; q.pop();
			if(vis[x])continue; vis[x] = true;
			for(int i = head[x]; i; i = e[i].net){
				int v = e[i].to; ll nc = e[i].cost + h[x] - h[v];
				if(e[i].val && dis[v] > dis[x] + nc){
					dis[v] = dis[x] + nc;
					pre[v] = x; pe[v] = i;
					if(!vis[v])q.push(pli(-dis[v], v));
				}
			}
		}
		return dis[t] != inf;
	}
	ll flow, cost;
	void mcmf(){
		spfa();
		while(dij()){
			ll f = inf;
			for(int i = 1; i <= t; ++i)h[i] += dis[i];
			for(int i = t; i != s; i = pre[i])f = min(f, e[pe[i]].val);
			for(int i = t; i != s; i = pre[i]){
				e[pe[i]].val -= f;
				e[pe[i] ^ 1].val += f;
			}
			flow += f; cost += f * h[t];
		}
	}
	void init(){
		n = read(), m = read();
		for(int i = 1; i <= n; ++i)a[i] = read();
		s = n + 1, t = s + 1;
		for(int i = 1; i <= n; ++i)if(a[i] != a[i - 1])link(s, i, a[i] - a[i - 1], 0);
		for(int i = 1; i < n; ++i)link(i, i + 1, a[i], 0); link(n, t, a[n], 0);
		for(int i = 1; i <= m; ++i){
			int l = read(), r = read(), w = read();
			link(l, r == n ? t : r + 1, 1, -w);
		}
		mcmf(); printf("%lld\n",-cost);
	}
}w;

int main(){
	// freopen("weed.in","r",stdin);
	// freopen("weed.out","w",stdout);
	w.init();
	return 0;
}

C. 基环树

考虑一棵树怎么做, \(f_{x, 0 / 1 / 2}\)

表示考虑完 \(x\) 子树内的,与 \(x\) 相连的链长度为 \(0 / 1 / 2\) 的最大价值

转移把 \(1 / 2\) 分别看成 \(+1 / -1\) 就是一个背包,而且只需要 \(-1 / 0 / 1\) 处的取值

随机化儿子顺序,这样只需要长度为根号级别的数组来做背包

考虑基环树,实际上随便找环上一条边断开然后当树做,分讨去掉的这条边是否使用,使用时两侧的链长,然后 \(DP\) 时候特殊处理一下即可

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 1e5 + 55;
const ll inf = 1e18;
mt19937 rd((ull)&maxn);
int n, m, ru, rv, rw, type;
vector<pii>e[maxn];
bool vis[maxn];
bool find_edge(int x, int fa){
	vis[x] = true;
	for(pii v : e[x]){
		if(v.first == fa)continue;
		if(vis[v.first]){ru = x, rv =  v.first, rw = v.second; return true;}
		if(find_edge(v.first, x))return true;
	}
	vis[x] = false; return false;
}
ll f[maxn][3], g[maxn], h[maxn];
void solve(int x, int fa){
	for(pii v : e[x])if(v.first != fa && (min(x, v.first) != min(ru, rv) || max(x, v.first) != max(ru, rv)))
		solve(v.first, x);
	int len = sqrt(e[x].size()) * 4 + 10, base = len >> 1;
	for(int i = 0; i <= len; ++i)g[i] = h[i] = -inf;
	g[base] = 0;
	for(pii v : e[x])if(v.first != fa && (min(x, v.first) != min(ru, rv) || max(x, v.first) != max(ru, rv))){
		for(int i = 0; i <= len; ++i)h[i] = g[i] + max(f[v.first][0], f[v.first][2] + v.second);
		for(int i = 0; i <= len; ++i)h[i + 1] = max(h[i + 1], g[i] + f[v.first][0] + v.second);
		for(int i = 1; i <= len; ++i)h[i - 1] = max(h[i - 1], g[i] + f[v.first][1] + v.second);
		for(int i = 0; i <= len; ++i)g[i] = h[i];
	}
	if(x == rv){
		if(type == 1)for(int i = 0; i <= len; ++i)h[i + 1] = g[i] + rw;
		if(type == 2)for(int i = 1; i <= len; ++i)h[i - 1] = g[i] + rw;
		if(type == 3)for(int i = 0; i <= len; ++i)h[i] = g[i] + rw;
		if(type == 4)h[base] = max(0ll, h[base]);
		for(int i = 0; i <= len; ++i)g[i] = h[i];
	}
	f[x][0] = g[base]; f[x][1] = g[base + 1]; f[x][2] = g[base - 1];
}
int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i){
		int u = read(), v = read(), w = read();
		e[u].push_back(pii(v, w));
		e[v].push_back(pii(u, w));
	}
	for(int i = 1; i <= n; ++i)shuffle(e[i].begin(), e[i].end(), rd);
	find_edge(1, 0); ll ans = 0;
	type = 1; solve(ru, 0); ans = max(ans, f[ru][0]);
	type = 2; solve(ru, 0); ans = max(ans, f[ru][1]);
	type = 3; solve(ru, 0); ans = max(ans, f[ru][2]);
	type = 4; solve(ru, 0); ans = max(ans, f[ru][0]);
	printf("%lld\n",ans);
	return 0;
}
posted @ 2023-06-15 20:14  Chen_jr  阅读(22)  评论(0编辑  收藏  举报