洛谷-9.24

P5937 [CEOI1999] Parity Game

这是一道好题。考察了扩展域并查集,总的来说,对我的启发如下:

        1、扩展域并查集是一种能在单个集合中记录多种关系的方法。常规的并查集在一个集合内只有一种集合,但是扩展域可以有多个,具体是通过+k*n(k表示要表达的关系数量),比如i与j表示一种关系,i与j+n表示另一种关系。

        2、扩展域并查集需要满足一种特定的关系——异或。即两个或多个关系经过传递后,最终形成的新关系需要等于前面若干关系的异或结果。

        3、并查集是可以使用离散化的,离散化不是随便用的。注意实现的一些细节。

        4、其他更多的做法——种类、带权并查集可以提供更多思路。

#include<bits/stdc++.h>
using namespace std;

const int N=5e3+10;
int fa[10*N],book[10*N];
void init(int n)
{
	for(int i=0;i<=2*n;i++)
		fa[i]=i;
}

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

void Union(int a,int b)
{
	int x=find(a),y=find(b);
	if(x!=y)
		fa[x]=y;
}

struct Node{
	int a;
	int b;
}node[5*N];
string S[5*N];

int main(void)
{
	int n,m;cin>>n>>m;
	int cnt=0;
	for(int i=1;i<=m;i++){
		cin>>node[i].a>>node[i].b>>S[i];
		node[i].a--;
		book[cnt++]=node[i].a;
		book[cnt++]=node[i].b;
	}
	
	
	sort(book,book+cnt);
	cnt=unique(book,book+cnt)-book;
	init(cnt);
	
	for(int i=1;i<=m;i++){
		int idxa=lower_bound(book,book+cnt,node[i].a)-book;
		int idxb=lower_bound(book,book+cnt,node[i].b)-book;
		node[i].a=idxa;
		node[i].b=idxb;
	}
		
	bool ok=true;
	int a,b;string str;
	for(int i=1;i<=m;i++){
		a=node[i].a,b=node[i].b;
		str=S[i];
		if(str=="odd"){
			if(find(a)==find(b)){
				cout<<i-1<<endl;
				ok=false;
				break;
			}
			Union(a,b+cnt);
			Union(b,a+cnt);	
		}else{
			if(find(a+cnt)==find(b)){
				cout<<i-1<<endl;
				ok=false;
				break;
			}
			Union(a,b);
			Union(a+cnt,b+cnt);
		}
	}
	if(ok)
		cout<<m<<endl;
}

P1955 [NOI2015] 程序自动分析

       和前一道题类似,但是思维更简单。

#include<bits/stdc++.h>
using namespace std;

const int N=1e6+10;
int fa[N];
int n;
struct Node{
	Node(){
	}
	Node(int u,int v,int d){
		x=u;
		y=v;
		e=d;
	}
	int x,y,e;
};
struct Node a[N];
vector<int> book;

void init(int n)
{
	for(int i=0;i<=n;i++)
		fa[i]=i;
}

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

bool cmp(struct Node A,struct Node B)
{
	return A.e>B.e;
}

int main(void)
{
	int T;cin>>T;
	
	while(T--){
		int cnt=0;
		bool ok=false;
		cin>>n;
		
		for(int i=0;i<n;i++){
			int u,v,d;
			cin>>u>>v>>d;
			a[i]={u,v,d};
			book.push_back(u);
			book.push_back(v);
		}
		
		sort(book.begin(),book.end());
		book.erase(unique(book.begin(),book.end()),book.end());
		init(book.size());
		for(int i=0;i<n;i++){
			a[i].x=lower_bound(book.begin(),book.end(),a[i].x)-book.begin();
			a[i].y=lower_bound(book.begin(),book.end(),a[i].y)-book.begin();
		}
		sort(a,a+n,cmp);
		for(int i=0;i<n;i++){
			int u=Find(a[i].x),v=Find(a[i].y);
			if(a[i].e){
				if(u!=v)
					fa[u]=v;
			}
			else if(u==v){
				printf("NO\n");
				ok=true;
				break;
			}
		}
		if(!ok) cout<<"YES"<<endl;
		book.clear();
	}	
}

P4552 [Poetize6] IncDec Sequence

对差分数组的考察,比较巧妙,可以记一下。

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+10;
int a[N];
long long sum[N]={0};

int main(void)
{
	int n;
	cin>>n;
	a[0]=0;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) sum[i]=a[i]-a[i-1];
	
	long long x=0,y=0;
	for(int i=2;i<=n;i++){
		if(sum[i]>0) x+=sum[i];
		else y-=sum[i];
	}
	cout<<max(x,y)<<endl;
	cout<<abs(x-y)+1;
}

P1314 [NOIP2011 提高组] 聪明的质监员

对前缀和和二分的考察。

#include<bits/stdc++.h>
using namespace std;

const int N=2e5+10;
int w[N],v[N];

typedef struct Node{
	Node(){
	}
	Node(int a,int b){
		l=a;
		r=b;
	}
	int l;
	int r;
}node;
node range[N];
int n,m;
long long s;
long long ans=1e12;
int sumw[N],sumv[N];

int check(long long mid)
{
	memset(sumw,0,sizeof(sumw));
	memset(sumv,0,sizeof(sumv));
	for(int i=1;i<=n;i++){
		if(w[i]>=mid){
			sumw[i]=sumw[i-1]+1;
			sumv[i]=sumv[i-1]+v[i];
		}else{
			sumw[i]=sumw[i-1];
			sumv[i]=sumv[i-1];
		}
	}
	long long y=0,Sum=0;
	for(int i=1;i<=m;i++){
		int l=range[i].l,r=range[i].r;
		Sum=(sumw[r]-sumw[l-1])*(sumv[r]-sumv[l-1]);
		y+=Sum;
	}
	
	ans=min(ans,abs(y-s));
	if(y>s) return -1;
	else if(y==s) return 0;
	else return 1; 
}

int main(void)
{
	cin>>n>>m>>s;
	for(int i=1;i<=n;i++){
		cin>>w[i]>>v[i];
	}
	for(int i=1;i<=m;i++){
		int a,b;
		cin>>a>>b;
		range[i]={a,b};
	}
	
	bool ok=false;
	int l=1,r=1e6;
	while(l<r){
		int mid=l+r>>1;
		int flag=check(mid);
		if(flag==1)
			r=mid;
		else if(flag==-1)
			l=mid+1;
		else{
			ok=true;
			cout<<0;
			break;
		}
	}
	if(!ok) cout<<ans;
}

总结。

差分:

        S[i]=a[i]-a[i-1]

前缀和:

        Sum[i]=Sum[i-1]+a[i]

前缀和的差分是原数组,差分的前缀和是原数组。差分通常用于对区间内整体的+或-,时间复杂度为O(1),前缀和通常用于对区间求和,时间为O(1)。

并查集:

        通常描述传递关系,注意传递关系与内部无关,比如[1,3]为odd,[3,6]为odd,只能说明[1,6]为even,但不能说明[2,4]是什么。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值