ABC408

D

link

首先考虑比较暴力的怎么做。
我们考虑一个前缀和的数组\(qzh_{0/1,i}\)代表\(1\)~\(i\)\(0/1\)的个数。
那么如果我们让区间\(l\)~\(r\)(因为区间长度可以为\(0\),所以\(r \geq l-1\))是\(1\),答案就是\(qzh_{1,l-1}+(qzh_{1,n}-qzh_{1,r})+(qzh_{0,r}-qzh_{0,l-1})\)
我们拆一下这个式子:\(qzh_{1,n}+(qzh_{1,l-1}-qzh_{0,l-1})+(qzh_{0,r}-qzh_{1,r})\)
其中,\(qzh_{1,n}\)是固定的,那么我们枚举\(l\),找到所有\(i \geq l-1\)中最小的\(qzh_{0,i}-qzh_{1,i}\),找最小做一个后缀最小值即可。

点击查看代码
#include<bits/stdc++.h>

using namespace std;

int n;
string s;
int q[2][200005];
int c[200005];

void solve(){
	
	cin >> n >> s;
	
	for(int i = 1;i <= n;++ i){
		q[1][i] = q[1][i-1];
		q[0][i] = q[0][i-1];
		if(s[i-1] == '1') q[1][i]++;
		else q[0][i]++;
		c[i] = q[0][i]-q[1][i];
	}
	
	int ans = 1e9;
	int res = c[n];
	for(int i = n;i >= 1;-- i){
		res = min(res,c[i-1]);
		ans = min(ans,q[1][i-1]-q[0][i-1]+res);
	}
	cout << ans+q[1][n] << endl;
	
	
}

signed main(){
	
	int _;cin >> _;
	while(_--) solve();
	
	return 0;
	
}

E

link

考虑贪心。
从高位到底位枚举每一个二进制位,考虑如果只走这一位是\(0\)的边能不能走到终点。
如果能,那么枚举后面的时候这一位也一定要是\(0\),否则枚举后面的时候这一位随意。那么如何实现这个呢,用一个变量(代码中是\(s\)),判断能不能走这个边的时候就拿边权和\(s\)\(&\),如果是\(0\)就可以走。这时,如果后面一定要是\(0\),那么把\(s\)的这一位设成\(1\),这样后面只有是\(0\)才能使最终结果是\(0\);如果后面随意,那么这一位是\(0\),这样后边不管是什么都可以变成\(0\)
如何判断能否走到终点?并查集。

点击查看代码
#include<bits/stdc++.h>

using namespace std;

int n,m;
struct edge{
	int u,v,w;
}a[200005];
int ans;
int fa[200005];

int find(int x){
	if(x == fa[x]) return x;
	else return fa[x] = find(fa[x]);
}

void merge(int x,int y){
	fa[find(x)] = find(y);
}

signed main(){
	
	cin >> n >> m;
	for(int i = 1;i <= m;++ i)
		cin >> a[i].u >> a[i].v >> a[i].w;
	
	int s = 0;
	for(int i = 29;i >= 0;-- i){
		s += (1<<i);
		for(int j = 1;j <= n;++ j) fa[j] = j;
		for(int j = 1;j <= m;++ j)
			if(!(a[j].w&s)) merge(a[j].u,a[j].v);
		if(find(1) != find(n))
			s -= (1<<i),ans += (1<<i); 
	}
	
	cout << ans;
	
	return 0;
	
} 

F

link

\(f_i\)代表从高度为\(i\)的木桩出发最多能走多少步。下文中\(wz_i\)代表高度为\(i\)的木桩的位置。
那么有转移:\(f_i = max(f_j)+1\)其中\(j\)要满足\(i-r \leq wz_j \leq i+r\)\(j \leq i-d\)
第一个条件好说,区间求\(max\),可以用线段树,把所有\(f_i\)加到\(wz_i\)这个位置上,第二个条件就没有那么简单了。
我们按高度从小到大求\(f\),那么我们每次只把高度在\(1\)~\(i-d\)中的加到序列上即可,然后再区间求\(max\)
最终答案是所有的\(f\)\(max\)

点击查看代码
#include<bits/stdc++.h>

#define int long long
#define ls x*2
#define rs x*2+1

using namespace std;

int n,D,R;
int a[500005];
int wz[500005];
int f[500005];

int val[2000005];

void update(int x){
	val[x] = max(val[ls],val[rs]);
}

void change(int x,int l,int r,int k,int s){
	if(l == r){
		val[x] = s;
		return;
	}
	int mid = (l+r)/2;
	if(k <= mid) change(ls,l,mid,k,s);
	else change(rs,mid+1,r,k,s);
	update(x);
}

int query(int x,int l,int r,int lx,int rx){
	if(lx <= l&&r <= rx) return val[x];
	int mid = (l+r)/2,ans = -1;
	if(lx <= mid) ans = max(ans,query(ls,l,mid,lx,rx));
	if(rx > mid) ans = max(ans,query(rs,mid+1,r,lx,rx));
	return ans;
}

signed main(){
	
	memset(val,-1,sizeof(val));
	cin >> n >> D >> R;
	for(int i = 1;i <= n;++ i)
		cin >> a[i],wz[a[i]] = i; 
	
	int ans = 0;
	for(int i = 1;i <= n;++ i){
		if(i-D > 0) change(1,1,n,wz[i-D],f[i-D]);
		f[i] = query(1,1,n,max(1ll,wz[i]-R),min(n,wz[i]+R))+1;
		ans = max(ans,f[i]);
	}
	cout << ans;
	
	return 0;
	
}
posted @ 2025-06-02 10:07  我的晴语表  阅读(24)  评论(0)    收藏  举报