海亮集训7.25周赛

A

image

签到

#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	x=(f ? -x : x);
	return;
}
string s;
int main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	cin>>s;
	if(s[0]=='0') cout<<s;
	else{
		cout<<s[0];
		for(int i=1;i<s.size();i++){
			cout<<(s[i]=='0' ? '1' : '0');
		}
	}
	return 0;
}
//^o^

B

image

暴力

#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	x=(f ? -x : x);
	return;
}
int l,r;
int main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	read(l),read(r);
	int ans=0;
	for(int i=r;i>=1;i--){
		int j=r/i;
		int a=i*j,b=i*(j-1);
		if(a>=l&&a<=r&&b>=l&&b<=r){
			ans=i;
			break;
		}
	}
	printf("%d",ans);
	return 0;
}
//^o^

C

image

因为向右移一次时只会减少第a[n-1]a[n]的异或值,增加a[1]a[n]的异或值

所以预处理每个数作为开头时的异或和,然后再询问过程中维护一个开头即可

#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
const int maxn=1e5+5;
void read(LL& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	x=(f ? -x : x);
	return;
}
LL a[maxn];
LL ans[maxn];
LL n,q;
int main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	read(n),read(q);
	for(int i=1;i<=n;i++){
		read(a[i]);
		if(i>1) ans[1]+=a[i]^a[i-1];
	}
	for(int i=1;i<n;i++){
		int j=n-i+1;
		ans[i+1]=ans[i]-(a[j]^a[j-1])+(a[j]^a[j%n+1]);
	}
	int st=1;
	printf("%lld ",ans[1]);
	LL x;
	while(q--){
		read(x);
		st=(st+x-1)%n+1;
		printf("%lld ",ans[st]);
	}
	return 0;
}
//^o^

D

image

不可忽视的取模

由于只向2取模,可以发现,一个串比另一个串若多上了偶数个1(可以为0个),其不相同个数肯定为偶数,即向2取模为零

用前缀和维护区间内1的数量即可

#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
const int maxn=2e5+5;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	x=(f ? -x : x);
	return;
}
int n,m;
string sa,sb;
int a[maxn],b[maxn];
int main(){
	//freopen("string2.in","r",stdin);
	//freopen(".out","w",stdout);
	read(n),read(m);
	cin>>sa>>sb;
	for(int i=1;i<=n;i++){
		a[i]=a[i-1]+(sa[i-1]=='1' ? 1 : 0);
	}
	for(int i=1;i<=m;i++){
		b[i]=b[i-1]+(sb[i-1]=='1' ? 1 : 0);
	}
	int q;
	read(q);
	int al,ar,bl,br;
	while(q--){
		read(al),read(ar),read(bl),read(br);
		//cout<<a[ar]-a[al-1]<<' '<<b[br]-b[bl-1]<<endl;
		if((a[ar]-a[al-1]-(b[br]-b[bl-1]))%2==0){
			printf("0\n");
		}
		else{
			printf("1\n");
		}
	}
	return 0;
}
//^o^

E

image

我的解法居然比官解好,还抢到了最优解%%%%%%

可以证明数列只有两种情况:

峰,谷,峰,谷,峰,谷,峰,...
谷,峰,谷,峰,谷,峰,谷,...

我们尝试模拟着来修改,让前一个数去限制后一个数

分类讨论:

1.当前的数不满足前一个数的限制,此时必须对这个数进行修改,同时修改要兼顾到后面一个数
2.当前的数满足前一个数的限制,此时对后一个数做出限制,然后跳过

可以证明修改后面的数一定是最优的,因为还可以兼顾到后面的后面

#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
const int maxn=1e5+5;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	x=(f ? -x : x);
	return;
}
int n;
int a[maxn];
int cal(bool tp){
	int lim=a[1];
	int ans=0;
	for(int i=2;i<=n;i++){
		if(tp){
			if(i>1&&a[i]<=lim){
				++ans;
				lim=max(lim,a[i+1])+1;
			}
			else{
				lim=a[i];
			}
		}
		else{
			if(i>1&&a[i]>=lim){
				++ans;
				lim=min(lim,a[i+1])-1;
			}
			else{
				lim=a[i];
			}
		}
		tp=!tp;
	}
	return ans;
}
int main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	read(n);
	for(int i=1;i<=n;i++){
		read(a[i]);
	}
	printf("%d",min(cal(1),cal(0)));
	return 0;
}
//^o^

今天时间不多了,先把后三道题放上来
H题本来都看出来了,结果DSU on tree太久没写忘了qwq

F

这道也是DSU on tree
image

G

image

H

image
先看出中序遍历,然后发现其选择不具有连续性,也就是说不一定只选子节点

选的是以它为根的所有子树中最大的两颗符合条件的子树

对每个点建立一个优先队列,存储以它为根的所有符合条件的子树大小

每次将子节点合并至父亲节点,考虑DSU on tree优化

记录每个点对应的优先队列,每次选择将队列长度小的合并到大的上

#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
const int maxn=1e5+5;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	x=(f ? -x : x);
	return;
}
priority_queue<int> q[maxn];
int t,n;
int head[maxn],nxt[maxn<<1],e[maxn<<1];
int rt[maxn];
int mp_cnt;
void init_mp(){
	memset(head,-1,sizeof(head));
	mp_cnt=-1;
}
void add_edge(int u,int v){
	e[++mp_cnt]=v;
	nxt[mp_cnt]=head[u];
	head[u]=mp_cnt;
}
int merge(int u,int v){
	if(q[u].size()<q[v].size()) swap(u,v);
	while(!q[v].empty()){
		q[u].push(q[v].top());
		q[v].pop();
	}
	return u;
}
void dfs(int u,int fa){
	for(int i=head[u];~i;i=nxt[i]){
		int v=e[i];
		if(v==fa) continue;
		dfs(v,u);
		rt[u]=merge(rt[u],rt[v]);
	}
	int ans=0;
	if(!q[rt[u]].empty()) ans+=q[rt[u]].top(),q[rt[u]].pop();
	if(!q[rt[u]].empty()) ans+=q[rt[u]].top(),q[rt[u]].pop();
	q[rt[u]].push(ans+1);
}
int main(){
	//freopen("tree2.in","r",stdin);
	read(t);
	while(t--){
		read(n);
		int u,v;
		init_mp();
		for(int i=1;i<n;i++){
			read(u),read(v);
			add_edge(u,v),add_edge(v,u);
		}
		for(int i=1;i<=n;i++){
			rt[i]=i;
			while(!q[i].empty()) q[i].pop();
		}
		dfs(1,1);
		printf("%d\n",q[rt[1]].top());
	}
	return 0;
}
//^o^
posted @ 2025-07-25 13:58  huangems  阅读(8)  评论(0)    收藏  举报