如何在 NOI2025 中速通 Cu

两天 T1 题解。


Day 1 T1 机器人

首先感觉这个东西看着就很像分层图的一个事情,然后我们先想一个暴力就是对于每个点,暴力建出来 \(k\) 层点,然后直接暴力连边,这个是很简单的。

然后我们思考一个事情就是那些层数超过我的度数的点,他除了内部可以转移出去以外就没有出边了,于是我们可以考虑给每个点建一个虚点,表示到达这个点,并且每个点只建度数层数的边,然后连边就还是类似的,但是我们要多连一条向终点对应的虚点的边,表示到达这个位置之后直接开摆。

然后还有就可能起点到终点所对应的边的编号超过了终点的度数,这种也很简单,直接连到最高的那一层并且加上到达终点后更改参数的代价就行了。

点击查看代码
//これも運命じゃないか
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define uint unsigned long long
#define Air
namespace io{
	inline int read(){
		int f = 1, t = 0; char ch = getchar();
		while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();}
		while(ch >= '0' && ch <= '9'){t = t * 10 + ch - '0'; ch = getchar();}
		return t * f;
	}
	inline void write(int x){
		if(x < 0){putchar('-'); x = -x;}
		if(x >= 10){write(x / 10);}
		putchar(x % 10 + '0');
	}
}
using namespace io;
int n, m, t;
const int N = 6e5 + 10;
int du[N];
int pre[N];
int a[N], b[N];
vector<int>e[N], v[N];
struct Edge{
	int x, y, z, id;
}edg[N];
int tot;
int sum[N];
struct Data{
	int x, y;
	friend bool operator < (Data x, Data y){
		return x.y > y.y;
	}
};
int dis[N];
bool vis[N];
priority_queue<Data> q;
void dijk(){
	q.push({1, 0});
	for(int i = 1; i <= m + n; i++){
		dis[i] = 1e18;
	}
	dis[1] = 0;
	while(q.size()){
		int now = q.top().x; q.pop();
		if(vis[now]) continue;
		vis[now] = 1;
		for(int i = 0; i < e[now].size(); i++){
			int y = e[now][i], z = v[now][i];
			if(dis[y] > dis[now] + z){
				dis[y] = dis[now] + z;
				q.push({y, dis[y]});
			}
		}
	}
}
signed main() {
#ifdef Air
	freopen("robot.in","r",stdin);
	freopen("robot.out","w",stdout);
#endif
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	read();
	n = read();
	m = read();
	t = read();
	for(int i = 1; i < t; i++){
		a[i] = read();
	}
	for(int i = 2; i <= t; i++){
		b[i] = read();
		sum[i] = sum[i - 1] + b[i];
	}
	for(int i = 1; i <= n; i++){
		du[i] = read();
		int k = du[i];
		int tmp = 0;
		pre[i] = pre[i - 1] + du[i];
		while(k--){
			int y = read(), z = read();
			edg[++tot] = {i, y, z, ++tmp};
		}
	}
	for(int i = 1; i <= m; i++){
		int x = edg[i].x, y = edg[i].y, z = edg[i].z, id = edg[i].id;
		int idx = pre[x - 1] + id, idy = min(pre[y - 1] + id, pre[y]);
		int tix = idx - pre[x - 1], tiy = idy - pre[y - 1];
		// if(y == 2){
		// 	cerr << x << ' ' << y << ' ' << idx << ' ' << idy << '\n';
		// }
		e[idx].push_back(y + m);
		v[idx].push_back(z);
		if(du[y]){
			e[idx].push_back(idy);
			v[idx].push_back(z + sum[tix] - sum[tiy]);
		}
	}
	for(int i = 1; i <= n; i++){
		int tmp = 0;
		for(int j = pre[i - 1] + 1; j < pre[i]; j++){
			tmp ++;
			e[j].push_back(j + 1);
			v[j].push_back(a[tmp]);
			e[j + 1].push_back(j);
			v[j + 1].push_back(b[tmp + 1]);
		}
	}
	// for(int i = 1; i <= n + m; i++){
	// 	for(int j = 0; j < e[i].size(); j++){
	// 		cerr << i << ' ' << e[i][j] << ' ' << v[i][j] << '\n';
	// 	}
	// }
	dijk();
	cout << 0 << ' ';
	for(int i = 2; i <= n; i++){
		if(dis[i + m] == 1e18){
			cout << -1 << ' ';
		}
		else{
			cout << dis[i + m] << ' ';
		}
	}
	return 0;
}

Day 2 T1 三目运算符

我们发现这个 \(f\) 直接看啥也看不出来,于是我们考虑打表,然后我们发现一共就两种可能的变化:

\(101 \to 100\)

\(110 \to 111\)

假如说不存在这种子串,那么就是一个不动点。

然后我们尝试刻画一下,我们发现一个 \(110 \to 111\) 操作之后,如果后面还有位置,那么必然还会有这个变化,为什么呢?

发现还是两种可能:

\(1101 \to 1110 \to 1111\)

\(1100 \to 1110 \to 1111\)

那么我们就可以按照最左边的 \(11\) 劈成两半,思考左边的,那么如果有 \(1\),连续的长度一定不会超过 \(2\),然后我们继续思考,发现这个操作最多在最开始操作一次,为什么呢?

我们发现这个操作的本质就是将 \(1010....01 \to 1000...01\),这个肯定只会在最开始操作。

然后我们就可以拼一下,左边如果有 \(101\),左边的答案就是 \(1\),右边的答案就是第一个 \(0\) 距离 \(n\) 有多远,两个东西取 \(\max\) 就行了。

区间反转怎么做?直接大力线段树。

然后如何找到某一个位置后的第一个 \(0\)?写一个类似线段树二分的状物。

特别不好写。

点击查看代码
//これも運命じゃないか
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define double long double
#define uint unsigned long long
#define Air
namespace io{
	inline int read(){
		int x; cin >> x; return x;
	}
	inline void write(int x){
		if(x < 0){putchar('-'); x = -x;}
		if(x >= 10){write(x / 10);}
		putchar(x % 10 + '0');
	}
}
using namespace io;
int n;
const int N = 4e5 + 10, INF = 1e9;
string s;
struct Segment{
	int l, r;
	bool laz;
	int sum0, sum1;
	bool op101, op010;
	int op11, op00;
	int fr0, fr1;
	int bk0, bk1; 
}seg[N * 4];
void upd(int p){
	seg[p].sum0 = seg[p * 2].sum0 + seg[p * 2 + 1].sum0;
	seg[p].sum1 = seg[p * 2].sum1 + seg[p * 2 + 1].sum1;
	if(seg[p].r - seg[p].l + 1 == 2){
		seg[p].fr0 = seg[p * 2].fr0;
		seg[p].fr1 = seg[p * 2 + 1].fr0;
		seg[p].bk0 = seg[p * 2].bk1;
		seg[p].bk1 = seg[p * 2 + 1].bk1;
	}
	else if(seg[p].r - seg[p].l + 1 == 3){
		seg[p].fr0 = seg[p * 2].fr0;
		seg[p].fr1 = seg[p * 2].fr1;
		seg[p].bk0 = seg[p * 2].bk1;
		seg[p].bk1 = seg[p * 2 + 1].bk1;
	}
	else {
		seg[p].fr0 = seg[p * 2].fr0;
		seg[p].fr1 = seg[p * 2].fr1;
		seg[p].bk0 = seg[p * 2 + 1].bk0;
		seg[p].bk1 = seg[p * 2 + 1].bk1;
	}
	seg[p].op101 = (seg[p * 2].op101 | seg[p * 2 + 1].op101);
	seg[p].op010 = (seg[p * 2].op010 | seg[p * 2 + 1].op010);
	//最靠左的11/00
	seg[p].op11 = min(seg[p * 2].op11, seg[p * 2 + 1].op11);
	seg[p].op00 = min(seg[p * 2].op00, seg[p * 2 + 1].op00);
	int p1, p2, p3, p4;
	p1 = seg[p * 2].bk0;
	p2 = seg[p * 2].bk1;
	p3 = seg[p * 2 + 1].fr0;
	p4 = seg[p * 2 + 1].fr1;
	if(p2 == 1 && p3 == 1){
		seg[p].op11 = min(seg[p].op11, seg[p * 2].r);
	}
	if(p2 == 0 && p3 == 0){
		seg[p].op00 = min(seg[p].op00, seg[p * 2].r);
	}
	if((p1 == 1 && p2 == 0 && p3 == 1) || (p2 == 1 && p3 == 0 && p4 == 1)){
		seg[p].op101 = 1;
	}
	if((p1 == 0 && p2 == 1 && p3 == 0) || (p2 == 0 && p3 == 1 && p4 == 0)){
		seg[p].op010 = 1;
	}
}
void spread(int p){
	if(seg[p].laz){
		seg[p * 2].laz ^= 1;
		swap(seg[p * 2].op010, seg[p * 2].op101);
		swap(seg[p * 2].op00, seg[p * 2].op11);
		swap(seg[p * 2].sum0, seg[p * 2].sum1);
		seg[p * 2].bk0 ^= 1;
		seg[p * 2].bk1 ^= 1;
		seg[p * 2].fr0 ^= 1;
		seg[p * 2].fr1 ^= 1;

		seg[p * 2 + 1].laz ^= 1;
		swap(seg[p * 2 + 1].op010, seg[p * 2 + 1].op101);
		swap(seg[p * 2 + 1].op00, seg[p * 2 + 1].op11);
		swap(seg[p * 2 + 1].sum0, seg[p * 2 + 1].sum1);
		seg[p * 2 + 1].bk0 ^= 1;
		seg[p * 2 + 1].bk1 ^= 1;
		seg[p * 2 + 1].fr0 ^= 1;
		seg[p * 2 + 1].fr1 ^= 1;

		seg[p].laz = 0;
	}
}

void build(int p, int l, int r){
	seg[p].l = l;
	seg[p].r = r;
	seg[p].sum0 = seg[p].sum1 = 0;
	seg[p].laz = 0;
	seg[p].op11 = seg[p].op00 = INF;
	seg[p].op101 = seg[p].op010 = 0;
	seg[p].bk0 = seg[p].bk1 = seg[p].fr0 = seg[p].fr1 = -1;
	if(l == r){
		if(s[l] == '0'){
			seg[p].sum0 = 1;
			seg[p].fr0 = seg[p].bk1 = 0; 
		}
		else{
			seg[p].sum1 = 1;
			seg[p].fr0 = seg[p].bk1 = 1; 
		}
		return ;
	}
	int mid = (l + r) >> 1;
	build(p * 2, l, mid);
	build(p * 2 + 1, mid + 1, r);
	upd(p);
}

void change(int p, int l, int r){
	if(seg[p].l >= l && seg[p].r <= r){
		seg[p].laz ^= 1;
		swap(seg[p].op010, seg[p].op101);
		swap(seg[p].op00, seg[p].op11);
		swap(seg[p].sum0, seg[p].sum1);
		seg[p].bk0 ^= 1;
		seg[p].bk1 ^= 1;
		seg[p].fr0 ^= 1;
		seg[p].fr1 ^= 1;
		return ;
	}
	spread(p);
	int mid = (seg[p].l + seg[p].r) >> 1;
	if(l <= mid){
		change(p * 2, l, r);
	}
	if(r > mid){
		change(p * 2 + 1, l, r);
	}
	upd(p);
}
int ask_0(int p, int x){
	if(!seg[p].sum0){
		return -1;
	}
	if(seg[p].l == seg[p].r) {
		return seg[p].l;
	}
	spread(p);
	int mid = (seg[p].l + seg[p].r) >> 1;
	int val = -1;
	if(x <= mid){
		val = ask_0(p * 2, x);
		if(val == -1){
			val = ask_0(p * 2 + 1, x);
		}
	}
	else{
		val = ask_0(p * 2 + 1, x);
	}
	return val;
}
ll get_res(){
	ll ans = 0;
	if(seg[1].op101){
		ans = 1;
	}
	int pos = seg[1].op11;
	if(pos == INF){
		return ans;
	}
	pos = ask_0(1, pos);
	if(pos != -1){
		ans = max(ans, (n - pos + 1) * 1ll);
	}
	return ans;
}
void work(){
	n = read();
	int q = read();
	cin >> s; s = ' ' + s;
	build(1, 1, n);
	// cerr << seg[1].op11 << ' ' << seg[1].op00 << ' ' << seg[1].op101 << ' ' << seg[1].op010 << '\n';
	ll res = 0;
	res = get_res();
	ll tmp = 0;
	while(q--){
		tmp ++;
		int l = read(), r = read();
		change(1, l, r);
		res ^= (tmp + 1) * get_res();
		// cerr << seg[1].op11 << ' ' << seg[1].op00 << ' ' << seg[1].op101 << ' ' << seg[1].op010 << '\n';
	}
	cout << res << '\n';
	// cerr << '\n';
}
signed main() {
#ifdef Air
	freopen("ternary.in","r",stdin);
	freopen("ternary.out","w",stdout);
#endif
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	read();
	int TCS = read();
	while(TCS --){
		work();
	}
	return 0;
}
posted @ 2026-05-18 16:36  Air2011  阅读(55)  评论(2)    收藏  举报