首师大附中集训第十二天综合模测

本文深入解析了算法竞赛中的四大经典题目,包括分则能成、明辨是非、第K大区间2及切方块。文章详细介绍了每道题目的解题思路与代码实现,特别是并查集、二分查找、前缀和与树状数组的应用,为读者提供了丰富的算法知识与实战经验。

正题

      第一题:分则能成

      这是之前写过的blog。

      第二题:明辨是非

      这题一开始想了很久,脑子里全是分块并查集,然后不断的把关系丢来丢去,很麻烦,其实只用考虑当前的一张关系图,有一些白边表示相等,有一些黑边表示不相等,首先我们可以把白边联通的点用并查集缩起来,然后考虑缩点之间的黑边,直接用set记录每一个联通块的黑出边就可以了,合并两个联通块的时候,按照黑边集合的大小启发式合并,就可以做到O(n\log_2^2 n)

      有几点需要注意,因为考试的时候也是最后检查才发现,当合并两个集合的时候,要把原来的黑边另一头指向的集合的黑边集合删除这一条边。具体可以看看代码,有点讲不清楚。

     

 #include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<set>
#include<map>
#include<algorithm>
using namespace std;

int n;
set<int> S[200010];
map<int,int> Q;
int f[200010];
struct ques{
	int x,y,opt;
}q[100010];
int t[200010];
set<int>::iterator it;

int findpa(int x){
	if(f[x]!=x) return f[x]=findpa(f[x]);
	return x;
}

int main(){
//	freopen("TrueFalse.in","r",stdin);
//	freopen("TrueFalse.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++) 
		scanf("%d %d %d",&q[i].x,&q[i].y,&q[i].opt),t[2*i-1]=q[i].x,t[i<<1]=q[i].y;
	sort(t+1,t+1+2*n);
	int last=0,tot=0,x,y,opt,fx,fy;
	for(int i=1;i<=2*n;i++) if(last!=t[i]) last=t[i],t[i]=++tot,Q[last]=tot;else t[i]=t[i-1];
	for(int i=1;i<=tot;i++) f[i]=i;
	for(int i=1;i<=n;i++) {
		x=Q[q[i].x],y=Q[q[i].y],opt=q[i].opt;
		fx=findpa(x),fy=findpa(y);
		if(opt==1){
			if(fx!=fy){
				if(S[fx].count(fy)) printf("NO\n");
				else{
					if(S[fx].size()<S[fy].size()) swap(fx,fy);
					for(it=S[fy].begin();it!=S[fy].end();it++){
						if(!S[fx].count(*it)) S[fx].insert(*it),S[*it].insert(fx);
						S[*it].erase(S[*it].lower_bound(fy));
					}
					S[fy].clear();
					f[fy]=fx;
					printf("YES\n");
				}
			}
			else printf("YES\n");
		}
		else{
			if(fx==fy) printf("NO\n");
			else{
				printf("YES\n");
				if(!S[fx].count(fy)) S[fx].insert(fy),S[fy].insert(fx);
			}
		}
	}
}

      第三题:第K大区间2

      很套路的就可以二分答案,然后计算一下值>=mid的区间有多少个。

      观察到一个性质,将一个奇区间内<中位数的染成-1,>=中位数的染成1.

      加起来>=1。那么我们就很容易可以判断一个区间的中位数是否>=mid。

      考虑前缀和,一个区间的和=sum_r -sum_{l-1}。所以sum_r-sum_{l-1}>=1

      sum_r-1>=sum_{l-1}

      所以我们就知道合法的sum_{l-1}的范围,同时如果要保证奇区间的话,加起来的和一定是奇数。

      所以,我们只要让sum_r-1,sum_{l-1}同奇偶就可以了。

      具体做法就是开两个树状数组维护一下即可。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define lowbit(x) (x&(-x))
using namespace std;

int n;
long long k;
int a[100010],p[100010];
int sumx[200010],sumy[200010];
const int data=100001;
int l,r;

void add(int *sum,int x){
	x+=data;
	while(x<=200005){
		sum[x]++;
		x+=lowbit(x);
	}
}

int get_sum(int *sum,int x){
	x+=data;
	int tot=0;
	while(x){
		tot+=sum[x];
		x-=lowbit(x);
	}
	return tot;
}

long long check(int x){
	for(int i=1;i<=n;i++) if(a[i]<x) p[i]=-1;else p[i]=1;
	for(int i=1;i<=200005;i++) sumx[i]=sumy[i]=0;
	int sum=0;
	long long ans=0;
	add(sumx,0);
	for(int i=1;i<=n;i++){
		sum+=p[i];
		if((sum-1)%2==0) ans+=get_sum(sumx,sum-1),add(sumy,sum);
		else ans+=get_sum(sumy,sum-1),add(sumx,sum);
	}
	return ans;
}

int main(){
//	freopen("Kth.in","r",stdin);
//	freopen("Kth.out","w",stdout);
	scanf("%d %lld",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	int l=0,r=(1ll<<31)-1,ans=0;
	while(l<=r){
		int mid=(l+r)/2;
		if(check(mid)>=k) l=(ans=mid)+1;
		else r=mid-1;
	}
	printf("%d\n",ans);
}

      第四题:切方块

      暂时还没有搞懂证明,今晚争取写出证明

      a=\frac{A}{gcd(A,B,C)},b=\frac{B}{gcd(A,B,C)},c=\frac{C}{gcd(A,B,C)}

      ans=gcd(A,B,C)^2*(ab+ac+bc-gcd(a,b)-gcd(b,c)-gcd(a,c))

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值