联合省选2021题解

本文介绍了两道算法题目,分别是矩阵游戏和图函数。矩阵游戏中,通过分析矩阵变换特性,得出解决方案;图函数问题则涉及最短路径计算和差分约束系统的处理。文章详细讲解了代码实现思路,并提供了完整的C++代码示例。
摘要由CSDN通过智能技术生成

突然发现500+好像挺容易的,有手就行
然而比赛的时候连400都没有
直接开始吧

卡牌游戏

https://www.luogu.com.cn/problem/P7514
瞎**贪心就完了,二分一手
code:

#include<bits/stdc++.h>
#define N 2000050
using namespace std;
int n, m, a[N], b[N], qma[N], qmi[N], mii[N], bb[N];
int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
	for(int i = 1; i <= n; i ++) scanf("%d", &b[i]), bb[i] = max(bb[i - 1], b[i]);
	//sort(c + 1, c + 1 + sz);
	int ans = a[n] - a[1];	
	m = min(n, m);
	//qma[m] = a[m]; 
	int mi = b[n], ma = b[n];
	for(int i = n; i >= n - m + 1; i --) {
		if(a[i] <= b[i]) break;
		mi = min(mi, b[i]), ma = max(ma, b[i]);
		int pos = m - (n - i + 1);
		qma[pos] = max(ma, a[i - 1]); qmi[pos] = mi; 
		int maa = max(qma[pos], a[1]), mii = min(qmi[pos], a[1]);
		ans = min(ans, maa - mii);
	}
	qma[m] = qmi[m] = a[n];
	mi = b[1];
	for(int i = 1; i < n; i ++) {
		mi = min(b[i], mi);
		mii[i] = min(mi, a[i + 1]);
	}
	for(int i = 1; i < n; i ++) mii[i] = max(mii[i], mii[i - 1]);
	
	for(int i = 1; i <= n; i ++) if(qma[i]){
	//	printf("%d %d\n", qma[i], qmi[i]);
		int pos = lower_bound(bb + 1, bb + 1 + n, qma[i]) - bb - 1;
		pos = min(pos, i);
		mi = min(mii[pos], qmi[i]), ma = qma[i];
	//	printf(" %d %d   %d    %d\n", ma, mi, pos, i);
		ans = min(ans, ma - mi);
	}
	
	sort(b + 1, b + 1 + n);
	ans = min(ans, b[n] - b[1]);
	printf("%d", ans);
	return 0;
} 

矩阵游戏

https://www.luogu.com.cn/problem/P7515
先乱搞出一个不考虑1000000限制的a数组
发现对于a的一行的所有数依次+1,-1,+1,-1,… b数组并不会变
对列同理
假设对第 i i i行加上的值为 r [ i ] r[i] r[i],列为 c [ i ] c[i] c[i]
显然增加的矩阵可以写成 r [ 1 ] + c [ 1 ] − r [ 1 ] + c [ 2 ] r [ 1 ] + c [ 3 ] r [ 2 ] − c [ 1 ] − r [ 2 ] − c [ 2 ] r [ 2 ] − c [ 3 ] r [ 3 ] + c [ 1 ] − r [ 3 ] + c [ 2 ] r [ 3 ] + c [ 3 ] \begin{matrix} r[1]+c[1] & -r[1]+c[2]& r[1]+c[3] \\ r[2]-c[1] & -r[2]-c[2]& r[2]-c[3] \\ r[3]+c[1] & -r[3]+c[2]& r[3]+c[3] \end{matrix} r[1]+c[1]r[2]c[1]r[3]+c[1]r[1]+c[2]r[2]c[2]r[3]+c[2]r[1]+c[3]r[2]c[3]r[3]+c[3]
假设上面的为 C C C
即要满足 0 < = A i , j + C i , j < = 1 0 6 0<=A_{i,j}+C_{i,j}<=10^6 0<=Ai,j+Ci,j<=106
移项
− A i , j < = C i , j < = 1 0 6 − A i , j -A_{i,j}<=C_{i,j}<=10^6-A_{i,j} Ai,j<=Ci,j<=106Ai,j
显然是个差分约束

但是上面那个矩阵有和又有差的不太好搞
发现两个都是+或者两个都是-的都是 ( i + j ) m o d 2 = = 0 (i+j) mod 2 == 0 (i+j)mod2==0的,对于这种把其中一个取反即可,我貌似是钦定 c j c_{j} cj为负
因为关系的是绝对值的大小,而不是具体的正负,但是同一行(或列)一定要同一
具体看代码吧
code:

#include<bits/stdc++.h>
#define N 605
#define ll long long
using namespace std;
const ll INF = (1e18);
ll g[N][N];
void insert(int u, int v, int c) {
	g[u][v] = c;
}
int in[N], vis[N], n, m;
ll dis[N];
queue<int> q;
int ok() {
	for(int i = 0; i <= n + m; i ++) vis[i] = 0, in[i] = 0, dis[i] = INF;
	while(q.size()) q.pop();
	dis[1] = 0; q.push(1); 
	while(q.size()) {
		int u = q.front(); q.pop();
		 vis[u] = 0;
		if(in[u] > n + m) return 0;
		for(int v = 1; v <= n + m; v ++) if(g[u][v] != INF){ ll c = g[u][v];
			if(dis[u] + c < dis[v]) {
				dis[v] = dis[u] + c; ++ in[v];
				if(!vis[v]) {
					vis[v] = 1; q.push(v);
				}
			}
		} 
	}
	return 1;
}
int a[N][N], b[N][N];
const int MX = 1000000;
void solve() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n + m; i ++)
		for(int j = 1; j <= n + m; j ++) g[i][j] = INF;
	for(int i = 1; i < n; i ++)
		for(int j = 1; j < m; j ++) scanf("%d", &b[i][j]);
	for(int i = n; i >= 1; i --)
		for(int j = m; j >= 1; j --) a[i][j] = b[i][j] - a[i + 1][j] - a[i][j + 1] - a[i + 1][j + 1];
	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= m; j ++) {
			if((i + j) & 1) {
				insert(i, j + n, a[i][j]);
				insert(j + n, i, MX - a[i][j]);
			} else {
				insert(i, j + n, MX - a[i][j]);
				insert(j + n, i, a[i][j]);
			}
		}
/*	for(int i = 1; i <= n + m; i ++) {
		for(int j = 1; j <= n + m; j ++) printf(" %d ", g[i][j]); printf("\n");
	}*/
	if(!ok()) {	printf("NO\n"); return ;}
	else {
		for(int i = 1; i <= n; i ++)
			for(int j = 1; j <= m; j ++) {
				if((i + j) & 1) a[i][j] += dis[i] - dis[j + n];
				else a[i][j] += dis[j + n] - dis[i];
			}
		printf("YES\n");
		for(int i = 1; i <= n; i ++) {
			for(int j = 1; j <= m; j ++) printf("%d ", a[i][j]); printf("\n");
		}
	} 
}
int t;
int main() {
	scanf("%d", &t);
	while(t --) solve();
	return 0;
}

图函数

https://www.luogu.com.cn/problem/P7516
这题代码竟然是最短的!!!

宝石

https://www.luogu.com.cn/problem/P7518
就分成从下往上和从下往上两种情况,然后拼起来
无脑做,应该是不难的,就是写起来有点烦
code:


#include<bits/stdc++.h>
#define N 400005
using namespace std;
struct edge {
	int v, nxt;
} e[N << 1];
int p[N], eid;
void init() {
	memset(p, -1, sizeof p);
	eid = 0;
}
void insert(int u, int v) {
	e[eid].v = v;
	e[eid].nxt = p[u];
	p[u] = eid ++;
}
int dep[N], fa[N][20], f[N][20], g[N][20], ff[N], a[N], b[N], cc[N], col[N], cid[N], ans[N];
int n, m, c, qq;
struct Q {
	int x, id, qi;
};
vector<Q> q[N];
void dfs(int u) { //if(u == 64904) printf("%d ", u);
	dep[u] = dep[fa[u][0]] + 1;
	ff[u] = ff[fa[u][0]];
	
	int id = cid[col[u]];
	if(id) {
	//	printf("   %d %d\n", u, id);
		if(id == 1) ff[u] = u;
		if(a[id + 1]) f[u][0] = a[id + 1];
		if(a[id - 1]) g[u][0] = a[id - 1];
		swap(a[id], b[u]);
	}
	//for(int i = 1; i <= 10; i ++) printf("%d ", a[i]); printf("\n");
	for(int i = p[u]; i + 1; i = e[i].nxt) { //if(u == 64904) printf("*%d*", i);
		int v = e[i].v;
		if(v == fa[u][0]) continue;
		fa[v][0] = u;
		//if(u == 64904) printf("*%d*", v), dfs(v);
		dfs(v);
	}
	
if(id) {
		swap(a[id], b[u]);
	}
}
int check(int x, int y, int z) {
	x = a[x];
	if(!x) return 0;
	for(int i = 0; i <= 18; i ++) {
		if((z >> i) & 1) x = g[x][i];
		if(dep[x] < dep[y]) return 0;
	}
	return dep[x] >= dep[y];
}
void dfss(int u) {
	int id = cid[col[u]];
	if(id) swap(a[id], b[u]);
/*	if(u == 5) {
		for(int i = 1; i <= m; i ++) printf("  %d ", a[i]); printf("\n");
	}*/
	for(int i = 0; i < q[u].size(); i ++) {
		int x = q[u][i].x, id = q[u][i].id, qi = q[u][i].qi;
		int l = 0, r = c - qi + 2;
		while(l + 1 < r) {
			int mid = (l + r) >> 1;
			if(check(qi + mid - 1, x, mid - 1)) l = mid;
			else r = mid;
		}// printf("^%d  %d   %d %d %d   %d %d^\n", id, l, qi + l - 1, x, l - 1, a[qi + l - 1], g[a[qi + l - 1]][0]);
		ans[id] += l;
	}
	
	for(int i = p[u]; i + 1; i = e[i].nxt) {
		int v = e[i].v;
		if(v == fa[u][0]) continue;
		dfss(v);
	}
	
	if(id) {
		swap(a[id], b[u]);
	}
}
int LCA(int x, int y) {
	if(dep[x] < dep[y]) swap(x, y);
	for(int i = 18; i >= 0; i --) if(dep[fa[x][i]] >= dep[y]) x = fa[x][i];
	if(x == y) return x;
	for(int i = 18; i >= 0; i --) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
	return fa[x][0];
}
int main() { 
//	freopen("gem.in","r",stdin);
//	freopen("gem.out","w",stdout);
	init();
	scanf("%d%d%d", &n, &m, &c);
	for(int i = 1; i <= c; i ++) scanf("%d", &cc[i]), cid[cc[i]] = i;
	for(int i = 1; i <= n; i ++) scanf("%d", &col[i]), b[i] = i;
	for(int i = 1; i < n; i ++) {
		int u, v;
		scanf("%d%d", &u, &v);
		insert(u, v), insert(v, u);
	} 
	dfs(1);
	for(int j = 1; j <= 18; j ++)
		for(int i = 1; i <= n; i ++)
			fa[i][j] = fa[fa[i][j - 1]][j - 1],
			f[i][j] = f[f[i][j - 1]][j - 1],
			g[i][j] = g[g[i][j - 1]][j - 1];
/*	for(int j = 0; j <= 5; j ++) {
		for(int i = 1; i <= n; i ++) printf("%d ", g[i][j]); printf("\n");
	}*/
	scanf("%d", &qq);
	for(int t = 1; t <= qq; t ++) {
		int u, v;
		scanf("%d%d", &u, &v);
		int lca = LCA(u, v);
	//	printf("%d %d  %d\n", u, v, lca);
		if(lca == v) {
			int x = ff[u];
			if(dep[x] >= dep[lca]) {
				for(int i = 18; i >= 0; i --) if(dep[f[x][i]] >= dep[lca]) x = f[x][i];
				ans[t] = cid[col[x]];
			} else ans[t] = 0;
			//printf("*%d*", t);
		} else 
		if(lca == u) {
			Q ha;
			ha.x = lca; ha.id = t, ha.qi = 1;
			q[v].push_back(ha);
		} else {
			int x = ff[u];
			if(dep[x] >= dep[lca]) { 
				for(int i = 18; i >= 0; i --) if(dep[f[x][i]] >= dep[lca]) x = f[x][i];
				Q ha;
				ha.x = lca; ha.id = t, ha.qi = cid[col[x]] + 1;
				ans[t] = ha.qi - 1;
				if(ha.qi <= c) q[v].push_back(ha);
			} else {
				Q ha;
			//	printf("*%d %d %d %d*\n", v, lca, t, 1);
				ha.x = lca; ha.id = t, ha.qi = 1;
				q[v].push_back(ha);
			}
		}
	}
	for(int i = 0; i <= n; i ++) b[i] = i, a[b[i]] = 0;
	dfss(1);
	for(int i = 1; i <= qq; i ++) printf("%d\n", ans[i]);
	return 0;
} 

to be continue……

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值