ABC 408

A:水
B:水
C:差分数组模板题

#include<iostream>
#include<cstdio>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include <sstream>
#include <vector>
using namespace std;
const double eps = 1e-10;
const int arrsize = 1e6+6;
int num[200002];
int lnum[200002],rnum[200002];
int pre[arrsize],ans;
int H,W,N,M,xb,S,tp,fg,lid,rid;

int main() {
	scanf("%d %d",&N,&M);
	xb = 0;fg = 0;
	ans = arrsize;
	for(int i=1;i<=M;i++){
		scanf("%d %d",&lid,&rid);
		pre[lid]++;
		pre[rid+1]--;
	}
	for(int i=1;i<=N;i++){
		xb += pre[i];
		if(xb < ans){
			ans = xb;
		}
	}
	printf("%d",ans);
	return 0;
}
/*
5
5 8 8 8 1
*/

D:不难,不过不用笔化公式就不太好想
首先可以确定的是,化简后的序列形式为0n+1m+0*k,且n,m,k为任意数
设序列中1组成的子序列从l开始r结束,并pre1[i]为前i位里1的个数
则需要改变的次数为(序列长为n)

\[pre1[l-1]+[(r-l+1)-(pre1[r]-pre1[l-1])]+pre1[n]-pre1[r] \]

化简后

\[pre1[n] + 2pre1[l-1]-(l-1)-(2pre1[r]-r) \]

枚举l,r形成双循环?那时间复杂度就超了
事实上,当我们枚举r时已经可以确定$$pre1[n] -(2pre1[r]-r)$$
同时可以提前处理得到r前最小的$$2pre1[l-1]-(l-1)$$
做完了

#include<iostream>
#include<cstdio>
using namespace std;
const int arrsize = 1e6+6;
int pre1[arrsize];
char s[arrsize];
int test, n;

int main() {
	scanf("%d", &test);
	while (test--) {
		scanf("%d %s", &n, s+1);  // 从s[1]开始存储
		
		// 计算pre1数组
		pre1[0] = 0;
		for (int i = 1; i <= n; i++) {
			pre1[i] = pre1[i-1] + (s[i] == '1');
		}
		
		int min_l = 0;  // 维护2*pre1[l-1]-(l-1)的最小值
		int ans = pre1[n];  // 初始化为全0的情况
		
		for (int r = 1; r <= n; r++) {
			// 更新min_l
			if (2 * pre1[r-1] - (r-1) < min_l) {
				min_l = 2 * pre1[r-1] - (r-1);
			}
			// 计算当前r对应的最优解并更新答案
			ans = min(ans, pre1[n] + min_l - (2 * pre1[r] - r));
		}
		
		printf("%d\n", ans);
	}
	return 0;
}

E:最短路要求的权值和最小改为权值或最小。
容易发现的性质有两个(或的性质)
1:只要答案中的某个权值在第i位是1,那么答案里必然该位是1。
2:答案与答案包括的每个权值的&的结果都是该权值
先设置一个最大值x,令每一位都为1表示取所有边;从最大位起考虑每一位,先将该位置为0,并将其&上所有边权,如果结果仍为边权则假定该边在该答案内,并使用并查集将点连通;
最后判断点1和点N是否连通,如果不连通则说明该位必然有1。
完了。

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 2e5 + 5;
int n, m, u, v, w, fa[maxn], x = (1 << 30) - 1;

struct Edge {
    int u, v, w;
};

vector<Edge> edges;

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

int main() {
    scanf("%d %d", &n, &m);
    edges.resize(m + 1);
    
    for (int i = 1; i <= m; i++) {
        scanf("%d %d %d", &u, &v, &w);
        edges[i] = {u, v, w};
    }
    
    for (int k = 29; k >= 0; k--) {
        x ^= 1 << k;//这里写的很漂亮,换我就写(1<<k)-1了
        for (int i = 1; i <= n; i++) fa[i] = i;
        
        for (int i = 1; i <= m; i++) {
            u = edges[i].u;
            v = edges[i].v;
            w = edges[i].w;
            if ((w & x) == w) fa[find(v)] = find(u);
        }
        
        if (find(1) != find(n)) x ^= 1 << k;
    }
    
    printf("%d", x);
    return 0;
}

F:第一次想出来F题,做了整一天,纪念一下。思路比较简单,首先抓住重要性质:H是一个1-N的序列,这样保证不会有相同的数字。
令ans[i]为i处可移动次数,基本思想就是,$$ans[i]= max(ans[j])+1,j∈[i-R,i+R],H[i]-H[j]>=D$$
考虑线段树维护ans以快捷完成单点修改和区间查询,将H数组中权值与位置打包存储并按权值从小到大排序,使用队列q暂缓存储D个最新值以保证当前ans[i]只能统计到合法的ans[j].
最后注意除了在线段树中找最大值还要在q中找!!!坑了我一下午

#include<iostream>
#include<cstdio>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include <sstream>
#include <vector>
#include <queue>
using namespace std;

const int MAXN = 500005;
int N, D, R;
int H[MAXN];
struct Node {
	int pos;
	int height;
	bool operator < (const Node &fk)const{
		return height < fk.height;
	}
}q[MAXN];

struct SegmentTree {
	int val;
	int ls;
	int rs;
}st[MAXN<<2];
void pushup_max(int rt){
	st[rt].val = max(st[rt<<1].val , st[rt<<1|1].val);
}
void build(int rt,int l,int r){
	st[rt].ls = l;
	st[rt].rs = r;
	if(l == r){
		st[rt].val = -1; 
		return ;
	}
	int mid = (l + r) >> 1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid + 1,r);
	pushup_max(rt);
}
int query_max(int rt,int l,int r){
	if(l <= st[rt].ls  && st[rt].rs <= r){
		return st[rt].val;
	}
	int mid = (st[rt].ls + st[rt].rs)>>1;
	//	cout<<rt<<" "<<st[rt].ls<<" "<<st[rt].rs<<" "<<l<<" "<<r<<" "<<mid<<endl;
	if(l > mid){
		//		cout<<"向右"<<(rt<<1|1)<<endl;
		return query_max(rt<<1|1,l,r);
	}
	if(r <= mid){
		//		cout<<"向左"<<(rt<<1)<<endl;
		return query_max(rt<<1,l,r);
	}
	//	cout<<"分段"<<endl;
	int a = query_max(rt<<1,l,mid);
	//	cout<<"从a回"<<a<<endl;
	int b = query_max(rt<<1|1,mid+1,r);
	//	cout<<"从b回"<<b<<endl;
	return max(a,b);
}
void sgupdate_max(int rt,int val,int pos){
	if(st[rt].ls == pos && st[rt].rs == pos){
		st[rt].val = val;
		return ;
	}
	int mid = (st[rt].ls + st[rt].rs)>>1;
	if(pos <= mid){
		sgupdate_max(rt<<1,val,pos);
	}else{
		sgupdate_max(rt<<1|1,val,pos);
	}
	pushup_max(rt);
	return;
}
int main() {
//	freopen("in.txt", "r", stdin);
//	freopen("mysol.txt", "w", stdout);
	scanf("%d %d %d", &N, &D, &R);
	for (int i = 1; i <= N; i++) {
		scanf("%d", &H[i]);
	}
	build(1,1,N);
	for (int i = 0; i < N; i++) {
		q[i] = {i + 1, H[i + 1]};
	}
	sort(q, q+N);
	queue<pair<int, int>> myq;
	for (int i = 0;i < N; i++){
		
		if((int)myq.size() == D){
			sgupdate_max(1,myq.front().second,myq.front().first);
			myq.pop();
		}
		int xb = q[i].pos;
		int l = max(1,xb - R);
		int r = min(N,xb + R);
		int tp = query_max(1,l,r);
		myq.push(make_pair(xb,tp+1));
		
	}
	int ass = -1;
	while(!myq.empty()){
		ass = max(ass,myq.front().second);
		myq.pop();
	}
	printf("%d", max(ass,query_max(1,1,N)));
	return 0;
}
posted @ 2025-06-02 23:06  Thomastine  阅读(28)  评论(0)    收藏  举报