Codeforces Round #594 (Div. 1) C、D

题目链接

C. Queue in the Train(线段树)


题意:

n n 个人在坐成一排,每个人有一个要装水的时间 t i t_i ,每个人装水时间为 p p ,规定当每个人的装水时间到了,他会先左往右看他左边有没人不在位置上,如果有,那么他不会去装水,等待下一时刻再次判断。如果同一时刻有多个人要去装水,则最左边的优先,问每一个人装完水的时间。

思路:

考虑装水的队列,当 p o s i pos_i 在队列中,则 大于 p o s i pos_i 的人不可能去排队,那么只要每次处理完 p o s i pos_i ,查看在其左边的人中找到最小的且小于当前时间的人加入队列即可,相当于排在其后面。那么只要判断一下没有人在队列中的情况,那么就是在剩余所有人中找到时间满足且在最左边的人,可以用线段树上二分来维护。

代码:

#include<bits/stdc++.h>
#define ls x<<1
#define rs x<<1|1
#define ll long long
#define inf 1e18
using namespace std;
const int N=1e5+10;
ll n,p;
struct node{
	ll mi,id;
	node operator + (const node &t)const{
		node temp;temp.mi=min(mi,t.mi);
		if(temp.mi==t.mi)temp.id=t.id;
		if(temp.mi==mi&&id<t.id)temp.id=id;
		return temp;
	}
}e[N*4];
ll arr[N],ans[N];
void built(int x,int l,int r){
	if(l==r){
		e[x].mi=arr[l];e[x].id=l;return ;
	}
	int mid=(l+r)/2;
	built(ls,l,mid);built(rs,mid+1,r);
	e[x]=e[ls]+e[rs];
}
void update(int x,int l,int r,int pos,ll val){
	if(l==r){
		e[x].mi=val;return;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)update(ls,l,mid,pos,val);
	else update(rs,mid+1,r,pos,val);
	e[x]=e[ls]+e[rs];
}
node query(int x,int l,int r,int LL,int RR){
	if(l>=LL&&r<=RR)return e[x];
	int mid=(l+r)/2;
	if(RR<=mid)return query(ls,l,mid,LL,RR);
	else if(LL>mid)return query(rs,mid+1,r,LL,RR);
	else return query(ls,l,mid,LL,RR)+query(rs,mid+1,r,LL,RR);
}
node query1(int x,int l,int r,ll val){//线段树二分
	if(l==r)return e[x];
	int mid=(l+r)/2;
	if(e[ls].mi<=val)return query1(ls,l,mid,val);
	else return query1(rs,mid+1,r,val);
}
node query2(int x,int l,int r,int LL,int RR,ll val){
	if(LL>RR)return (node){-1,-1};
	if(l>=LL&&r<=RR){
		if(e[x].mi<=val)return query1(x,l,r,val);
		else return (node){-1,-1};
	}
	int mid=(l+r)/2;
	node ans=query2(ls,l,mid,LL,RR,val);
	if(ans.id==-1)ans=query2(rs,mid+1,r,LL,RR,val);
	return ans;
}
queue<int>q;
node temp;
void solve(){
	built(1,1,n);ll t=0;
	for(int i=1;i<=n;i++){
		if(q.empty()){//没有人提前去排队
			temp=e[1];
			if(temp.mi>t){//无人要装水,时间增加
				ans[temp.id]=temp.mi+p;t=ans[temp.id];
				update(1,1,n,temp.id,inf);int pos=temp.id;
				temp=query(1,1,n,1,pos);
				if(temp.mi<t)q.push(temp.id);
				continue;
			}
			temp=query2(1,1,n,1,n,t);//时间小于t且在最左边
			ans[temp.id]=t+p;t=t+p;
			update(1,1,n,temp.id,inf);int pos=temp.id;
			temp=query(1,1,n,1,pos);
			if(temp.mi<t)q.push(temp.id);
		}else{//有人在队列中先处理
			int now=q.front();q.pop();
			update(1,1,n,now,inf);t=t+p;ans[now]=t;
			temp=query(1,1,n,1,now);
			if(temp.mi<t)q.push(temp.id);
		}
		
	}
}
int main()
{
	//freopen("H:\\c++1\\in.txt","r",stdin);
	//freopen("H:\\c++1\\out.txt","w",stdout);
	scanf("%lld%lld",&n,&p);
	for(int i=1;i<=n;i++)scanf("%lld",&arr[i]);
	solve();
	for(int i=1;i<=n;i++){
		printf("%lld%c",ans[i],i==n?'\n':' ');
	}
	return 0;
}

D. Catowice City (2-sat)

题意:

n n 个人,每个人家有一只猫。每个人都认识一些猫(其中肯定包括自己家的猫)。选出 a a 个人和 b b 只猫 ( a , b 1 ) (a,b≥1) 。使得 a + b = n a+b=n 且选出的人和猫都互不认识。求方案

思路:

可以观察到,这是一个 2 s a t 2-sat 问题,对于给定的每一条认识关系,我们可以通过建边使用强连通分量来解,但是由于本题存在一个特殊的条件,对于每一个人必定认识自家的猫,那么我们假设人用下标 1 1 表示,猫用下标 2 2 表示可以得到对于 x 1 , x 2 x1,x2 两个必须要要选择一个,如若不然因为 a + b = n a+b=n 那么必然存在 y 1 , y 2 y1,y2 同时选择的情况就出现矛盾。由此一人一猫必选其一,那么如果存在一个认识关系为 ( x 1 , y 2 ) (x1,y2) ,那么我们假设选择了 x 1 x1 就必然要选择 x 2 x2 ,因为 y 2 y2 不能选,那么我可以枚举选不选第一个人的这两种情况,通过这些关系推出选择方案,选择其中合法的方案即可。 (要注意下标问题,容易弄混)

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
vector<int>e1[N],e2[N];
int n,m,T,cnt1,cnt2,vis1[N],vis2[N];
void dfs1(int u){
	vis1[u]=1;cnt1++;//选择人u
	for(int v:e1[u]){
		if(vis1[v])continue;
		dfs1(v);
	}
}
void dfs2(int u){
	vis2[u]=1;cnt2++;//选择猫u
	for(int v:e2[u]){
		if(vis2[v])continue;//连接的v是人,但对应的v猫则必选
		dfs2(v);
	}
}
int main()
{
	//freopen("H:\\c++1\\in.txt","r",stdin);
	//freopen("H:\\c++1\\out.txt","w",stdout);
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)e1[i].clear(),e2[i].clear(),vis1[i]=vis2[i]=0;
		for(int i=1;i<=m;i++){
			int u,v;
			scanf("%d%d",&u,&v);
			if(u!=v)e1[u].push_back(v),e2[v].push_back(u);
		}	
		cnt1=cnt2=0;
		dfs1(1);
		dfs2(1);
		if(cnt1==n&&cnt2==n){
			puts("No");continue;
		}
		puts("Yes");
		if(cnt1!=n){
			printf("%d %d\n",cnt1,n-cnt1);
			for(int i=1;i<=n;i++)if(vis1[i]==1)printf("%d ",i);puts("");
			for(int i=1;i<=n;i++)if(vis1[i]!=1)printf("%d ",i);puts("");
			continue;
		}
		if(cnt2!=n){
			printf("%d %d\n",n-cnt2,cnt2);
			for(int i=1;i<=n;i++)if(vis2[i]!=1)printf("%d ",i);puts("");
			for(int i=1;i<=n;i++)if(vis2[i]==1)printf("%d ",i);puts("");
			continue;
		}
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40400202/article/details/102684918