2022NOIP A层联测19

A. 皮胚

DP fl,r 表示 s,t 分别匹配到 l,r 是否可行

转移就是简单分类讨论

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;


const int maxn = 2005;
char s[maxn], t[maxn];
bool f[maxn][maxn];
int n, m;
void solve(){
	scanf("%s%s",s + 1, t + 1);
	n = strlen(s + 1);
	m = strlen(t + 1);
	memset(f, 0, sizeof(f));
	f[0][0] = 1;
	for(int i = 0; i <= n; ++i)
		for(int j = 0; j <= m; ++j)
			if(f[i][j]){
				if(t[j + 1] == '.')f[i + 1][j + 1] = 1;
				if(t[j + 1] == s[i + 1])f[i + 1][j + 1] = 1;
				if(t[j + 1] == '*'){
					f[i][j + 1] = 1;
					if(s[i + 1] == s[i])f[i + 1][j] = 1;
				}
			}
	int ans = 0;
	for(int i = 1; i <= n; ++i)ans += f[i][m];
	printf("%d\n",ans);
}

int main(){
	freopen("match.in","r",stdin);
	freopen("match.out","w",stdout);
	int t; scanf("%d",&t);
	for(int i = 1; i <= t; ++i)solve();
	return 0;
}

B. 核冰

考场想的跟题解类似,对退位的维护不清楚具体怎么搞,而且好像很麻烦

这里使用了 chino 巨佬的做法

考虑维护进位前的数,

如果 +1 那么到第一个奇数/ 0 的一段 +1,

退位可以直接逆操作,即到第一个偶数/1 的一段 1

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

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

const int maxn = 500100, mx = 5e5 + 25;
int cnt[maxn], n, m, a[maxn];
int ans = 0;

struct seg{
	struct node{
		bool all0, all1;
		int mi, tag;
	}t[maxn << 2 | 1];
	void push_up(int x){
		int ls = x << 1, rs = x << 1 | 1;
		t[x].all0 = t[ls].all0 & t[rs].all0;
		t[x].all1 = t[ls].all1 & t[rs].all1;
		t[x].mi = min(t[ls].mi, t[rs].mi);
	}
	void upd(int x, int w){
		t[x].mi += w;
		t[x].tag += w;
		if(w & 1)swap(t[x].all0, t[x].all1);
	}
	void push_down(int x){
		upd(x << 1, t[x].tag);
		upd(x << 1 | 1, t[x].tag);
		t[x].tag = 0;
	}
	void built(int x, int l, int r){
		if(l == r){
			t[x].mi = cnt[l];
			t[x].all1 = cnt[l] & 1;
			t[x].all0 = !(cnt[l] & 1);
			return;
		}
		int mid = (l + r) >> 1;
		built(x << 1, l, mid);
		built(x << 1 | 1, mid + 1, r);
		push_up(x);
	}
	int add(int x, int l, int r, int pos){
		if(l >= pos){
			if(t[x].all0 && t[x].mi)return upd(x, 1), r;
			if(l == r){
				if(!t[x].mi)t[x].mi = 1, t[x].all0 = false, t[x].all1 = true, ++ans;
				else upd(x, 1);
				return r - 1;
			}
		}
		if(t[x].tag)push_down(x);
		int mid = (l + r) >> 1, mr = 0;
		if(pos <= mid)mr = add(x << 1, l, mid, pos);
		if(pos > mid || mr == mid) mr = add(x << 1 | 1, mid + 1, r, pos);
		push_up(x);
		return mr;
	}
	int del(int x, int l, int r, int pos){
		if(l >= pos){
			if(t[x].all1 && t[x].mi > 1)return upd(x, -1), r;
			if(l == r){
				if(t[x].mi == 1)t[x].mi = 0, t[x].all0 = true, t[x].all1 = false, --ans;
				else upd(x, -1);
				return r - 1;
			}
		}
		if(t[x].tag)push_down(x);
		int mid = (l + r) >> 1, mr;
		if(pos <= mid)mr = del(x << 1, l, mid, pos);
		if(pos > mid || mr == mid) mr = del(x << 1 | 1, mid + 1, r, pos);
		push_up(x);
		return mr;
	}
}t;
void change(){
	int x = read(), y = read();
	t.del(1, 1, mx, a[x]);
	a[x] = y; t.add(1, 1, mx, a[x]);
}
int main(){
	// freopen("merge.in","r",stdin);
	// freopen("merge.out","w",stdout);
	n = read(), m = read();
	for(int i = 1; i <= n; ++i)++cnt[a[i] = read()];
	for(int i = 1; i < mx; ++i){
		cnt[i] += (cnt[i - 1] - 1) / 2;
		ans += cnt[i] > 0;
	}
	t.built(1, 1, mx);
	for(int i = 1; i <= m; ++i){
		int op = read();
		if(op & 1)change();
		else printf("%d\n",ans);
	}
	return 0;
}

C. 方珍

二分答案,把 >=ansmex 都看成 ans

那么就可以双指针找出 mex<ans 的区间个数

那么这时就是 n2log

考虑按照 wi 降序排序,每次尝试把答案 +1, 这样答案最多增加 n+1

复杂度就变成 n2

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

ull read(){
	ull 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 = 10005;
int n, k[maxn], w[maxn], m[maxn], a[maxn];
int p[maxn], nk, nm;
ull seed0[maxn], seed;
ull _rand(){
	seed^=seed<<13;
	seed^=seed>>7;
	seed^=seed<<17;
	return seed;
}
void read_test(){
    for(int i=1; i<=n; ++i) a[i] = _rand() % nm;
}
int cnt[maxn];
bool check(int ans){
	int now = 0, l = 1, ex = 0;
	for(int i = 0; i <= nm + 1; ++i)cnt[i] = 0;
	for(int r = 1; r <= n; ++r){
		if(a[r] < ans)ex += cnt[a[r]] == 0, ++cnt[a[r]];
		while(ex >= ans){
			while(a[l] >= ans)++l;
			--cnt[a[l]]; 
			ex -= cnt[a[l]] == 0;
			++l;
		}
		now += r - l + 1;
		if(now >= nk)return false;
	}
	return now < nk;
}
bool cmp(int x, int y){return w[x] > w[y];}
int main(){
	// freopen("mex.in","r",stdin);
	// freopen("mex.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i)p[i] = i;
	for(int i = 1; i <= n; ++i){ k[i] = read(); w[i] = read(); m[i] = read(); seed0[i] = read();}
	sort(p + 1, p + n + 1, cmp);
	int ans = w[p[1]];
	for(int i = 1; i <= n; ++i){
		int now = p[i];
		if(ans >= min(n, m[now]) + w[now] + 1)continue;
		nk = k[now]; nm = m[now]; seed = seed0[now];
		read_test();
		while(check(ans + 1 - w[now]))++ans;
	}
	printf("%d\n",ans);
	return 0;
}

D. 术劣

一个结论, 等差数列打乱公差 d=gcd(abs(a2a1),abs(a3a2)...)

那么对于一个区间,如果 maxmin=(rl)d 就是合法的

变形成为 maxmin+ld=rd

于是上线段树套路,移动 r, 叶节点维护 [x,r] 区间信息

max,min 单调栈可以简单维护

考虑如何维护 ld

每个位置的 gcd 最多变化 log 次,那么可以每次暴力修改

用并查集维护 gcd 相同的连续段,每次变化时把段内位置都单点修改即可

因为我们取的是后缀 gcd 所以集合的个数也是最多 log

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
#define int long long
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 = 200005;
int a[maxn], n;
struct seg{
	struct node{
		int tag, mi, cntmi;
	}t[maxn << 2 | 1];
	void push_up(int x){
		t[x].mi = min(t[x << 1].mi, t[x << 1 | 1].mi);
		t[x].cntmi = 0;
		if(t[x].mi == t[x << 1].mi)t[x].cntmi += t[x << 1].cntmi;
		if(t[x].mi == t[x << 1 | 1].mi)t[x].cntmi += t[x << 1 | 1].cntmi;
	}
	void push_down(int x){
		int ls = x << 1, rs = x << 1 | 1;
		t[ls].mi += t[x].tag;
		t[ls].tag += t[x].tag;
		t[rs].mi += t[x].tag;
		t[rs].tag += t[x].tag;
		t[x].tag = 0;
	}
	void built(int x, int l, int r){
		if(l == r){
			t[x].cntmi = 1;
			return;
		}
		int mid = (l + r) >> 1;
		built(x << 1, l, mid);
		built(x << 1 | 1, mid + 1, r);
		push_up(x);
	}
	void modify(int x, int l, int r, int L, int R, int val){
		if(L <= l && r <= R){
			t[x].mi += val;
			t[x].tag += val;
			return;
		}
		if(t[x].tag)push_down(x);
		int mid = (l + r) >> 1;
		if(L <= mid)modify(x << 1, l, mid, L, R, val);
		if(R > mid)modify(x << 1 | 1, mid + 1, r, L, R, val);
		push_up(x);
	}
	void modify_one(int x, int l, int r, int pos, int val){
		if(l == r){
			t[x].mi += val;
			return;
		}
		if(t[x].tag)push_down(x);
		int mid = (l + r) >> 1;
		if(pos <= mid)modify_one(x << 1, l, mid, pos, val);
		else modify_one(x << 1 | 1, mid + 1, r, pos, val);
		push_up(x);
	}
	int query(int x, int l, int r, int L, int R, int val){
		if(L <= l && r <= R){
			assert(t[x].mi >= val);
			return t[x].mi == val ? t[x].cntmi : 0;
		}
		if(t[x].tag)push_down(x);
		int mid = (l + r) >> 1, ans = 0;
		if(L <= mid)ans += query(x << 1, l, mid, L, R, val);
		if(R > mid)ans += query(x << 1 | 1, mid + 1, r, L, R, val);
		return ans;
	}
}t;
int stmi[maxn], top1, stmx[maxn], top2;
int f[maxn], gg[maxn], l[maxn];
int fa(int x){return x == f[x] ? x : f[x] = fa(f[x]);}
void merge(int x, int y){
	x = fa(x); y = fa(y);
	if(x == y)return;
	l[x] = min(l[x], l[y]);
	f[y] = x;
}
ll ans = 0;
#undef int
int main(){
	#define int long long
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	n = read(); for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= n; ++i)f[i] = i, l[i] = i;
	t.built(1, 1, n);
	for(int r = 1; r <= n; ++r){
		while(top1 && a[stmi[top1]] >= a[r]){
			t.modify(1, 1, n, stmi[top1 - 1] + 1, stmi[top1], a[stmi[top1]] - a[r]);
			--top1;
		}
		while(top2 && a[stmx[top2]] <= a[r]){
			t.modify(1, 1, n, stmx[top2 - 1] + 1, stmx[top2], a[r] - a[stmx[top2]]);
			--top2;
		}
		stmi[++top1] = r; stmx[++top2] = r;
		if(r > 1){
			int now = abs(a[r] - a[r - 1]);
			gg[r - 1] = now; t.modify_one(1, 1, n, r - 1, now * (r - 1));
			int las = r - 1;
			for(int p = r - 2; p >= 1; --p){
				p = fa(p);
				now = __gcd(gg[p], now);
				if(now != gg[p]){
					for(int j = l[p]; j < las; ++j)t.modify_one(1, 1, n, j, j * now - j * gg[p]);
					gg[p] = now;
				}
				if(gg[p] == gg[fa(las)])merge(p, las);
				p = las = l[fa(p)];
			}
		}
		++ans;
		int las = r;
		for(int p = r - 1; p >= 1; --p){
			p = fa(p);
			ans += t.query(1, 1, n, l[p], las - 1, gg[p] * r);
			p = las = l[fa(p)];
		}
		printf("%lld ",ans);
	}

	return 0;
}
posted @   Chen_jr  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示