正题
第一题:分则能成
这是之前写过的blog。
第二题:明辨是非
这题一开始想了很久,脑子里全是分块并查集,然后不断的把关系丢来丢去,很麻烦,其实只用考虑当前的一张关系图,有一些白边表示相等,有一些黑边表示不相等,首先我们可以把白边联通的点用并查集缩起来,然后考虑缩点之间的黑边,直接用set记录每一个联通块的黑出边就可以了,合并两个联通块的时候,按照黑边集合的大小启发式合并,就可以做到。
有几点需要注意,因为考试的时候也是最后检查才发现,当合并两个集合的时候,要把原来的黑边另一头指向的集合的黑边集合删除这一条边。具体可以看看代码,有点讲不清楚。
#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。
考虑前缀和,一个区间的和。所以
。
所以我们就知道合法的的范围,同时如果要保证奇区间的话,加起来的和一定是奇数。
所以,我们只要让同奇偶就可以了。
具体做法就是开两个树状数组维护一下即可。
#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);
}
第四题:切方块
暂时还没有搞懂证明,今晚争取写出证明

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

被折叠的 条评论
为什么被折叠?



