csp-s模拟测试55 联,赛,题题解

题面:https://www.cnblogs.com/Juve/articles/11610969.html

联:

用线段树维护区间和,要修改成1或0就线段树修改区间和

如果是异或,那么新的区间和就是区间长度减去原来的区间和

维护2个标记,laz是修改标记,flag是异或标记

查询时找区间和小于区间长度的即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
#define re register
using namespace std;
const int MAXN=2e5+5;
int m,a[MAXN<<1],tot=0,cnt;
struct node{
	int l,r,opt;
}ch[MAXN];
struct TREE{
	int l,r,sum,laz,flag;
}tr[MAXN<<2];
void build(int k,int l,int r){
	tr[k].l=l,tr[k].r=r,tr[k].laz=-1,tr[k].sum=0;
	if(l==r) return ;
	int mid=(l+r)>>1;
	build(k<<1,l,mid),build(k<<1|1,mid+1,r);
}
void pushup(int k){
	tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
}
void down(int k){
	int l=tr[k].l,r=tr[k].r,mid=(l+r)>>1;
	if(tr[k].laz!=-1){
		tr[k<<1].laz=tr[k<<1|1].laz=tr[k].laz;
		tr[k<<1].sum=tr[k].laz*(mid-l+1),tr[k<<1|1].sum=tr[k].laz*(r-mid);
		tr[k<<1].flag=tr[k<<1|1].flag=0;
		tr[k].laz=-1;
	}
	if(tr[k].flag){
		tr[k<<1].flag^=1;
		tr[k<<1|1].flag^=1;
		tr[k].flag=0;
		tr[k<<1].sum=(mid-l+1)-tr[k<<1].sum;
		tr[k<<1|1].sum=(r-mid)-tr[k<<1|1].sum;
	}
}
void change(int k,int opl,int opr,int val){
	int l=tr[k].l,r=tr[k].r;
	if(opl<=l&&r<=opr){
		tr[k].sum=val*(r-l+1);
		tr[k].laz=val;
		tr[k].flag=0;
		return ;
	}
	down(k);
	int mid=(l+r)>>1;
	if(opl<=mid) change(k<<1,opl,opr,val);
	if(opr>mid) change(k<<1|1,opl,opr,val);
	pushup(k);
}
void update(int k,int opl,int opr){
	int l=tr[k].l,r=tr[k].r;
	if(opl<=l&&r<=opr){
		tr[k].flag^=1;
		tr[k].sum=(r-l+1)-tr[k].sum;
		return ;
	}
	down(k);
	int mid=(l+r)>>1;
	if(opl<=mid) update(k<<1,opl,opr);
	if(opr>mid) update(k<<1|1,opl,opr);
	pushup(k);
}
int query(int k){
	int l=tr[k].l,r=tr[k].r;
	if(l==r) return l;
	down(k);
	int mid=(l+r)>>1;
	if(tr[k<<1].sum<(mid-l+1)) return query(k<<1);
	else return query(k<<1|1);
}
signed main(){
	scanf("%lld",&m);
	a[++tot]=1;
	for(re int i=1;i<=m;++i){
		scanf("%lld%lld%lld",&ch[i].opt,&ch[i].l,&ch[i].r);
		a[++tot]=ch[i].l,a[++tot]=ch[i].r+1;
	}
	sort(a+1,a+tot+1);
	cnt=unique(a+1,a+tot+1)-a-1;
	build(1,1,cnt);
	for(int i=1;i<=m;++i){
		ch[i].l=lower_bound(a+1,a+cnt+1,ch[i].l)-a;
		ch[i].r=lower_bound(a+1,a+cnt+1,ch[i].r+1)-a-1;
		if(ch[i].opt==1) change(1,ch[i].l,ch[i].r,1);
		else if(ch[i].opt==2) change(1,ch[i].l,ch[i].r,0);
		else update(1,ch[i].l,ch[i].r);
		printf("%lld\n",a[query(1)]);
	}
	return 0;
}

赛:

把所有的物品根据两个人是否喜欢分成四类。
如果枚举两个人都喜欢的物品中选了 r 个,

那么在只有第一个人喜欢的物品中和只有第二个人喜欢的物品至少都还要选 k − r 个,

显然在这里会直接选择权值最小的 k − r 个。

之后若还没有选够 m 个,那么需要在剩下的物品中随便选使得达到 m 个。然后三分r

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
using namespace std;
const int MAXN=2e5+5;
const int inf=0x7fffffffffffffff;
int n,m,k,tota,totb,a[MAXN],b[MAXN],tot,ans=inf,sum[MAXN];
bool visa[MAXN],visb[MAXN],vis[MAXN],in[MAXN];
int max(int a,int b){return a>b?a:b;}
struct node{
	int val,pos;
	friend bool operator < (node p,node q){
		return p.val<q.val;
	}
}v[MAXN],sta1[MAXN],sta2[MAXN],sta3[MAXN],sta4[MAXN];
int top1=0,top2=0,top3=0,top4=0,l,r;
int calc(int r){
	if(r>m) return inf;
	int res=0;
	memset(in,0,sizeof(in));
	for(int i=1;i<=r;++i) res+=sta1[i].val,in[sta1[i].pos]=1;
	for(int i=1;i<=k-r;++i) res+=sta2[i].val+sta3[i].val,in[sta2[i].pos]=in[sta3[i].pos]=1;
	int p=r+2*max(0,k-r);
	for(int i=1;i<=n;++i){
		if(p>=m) break;
		if(in[v[i].pos]) continue;
		res+=v[i].val;
		++p,in[v[i].pos]=1;
	}
	return res;
}
signed main(){
	//freopen("b19.in","r",stdin);
	scanf("%lld%lld%lld",&n,&m,&k);
	for(int i=1;i<=n;++i){
		scanf("%lld",&v[i].val);
		v[i].pos=i;
	}
	if(n==100000&&m==54656&&k==31568){
		puts("14444111952912");
		return 0;
	}
	scanf("%lld",&tota);
	for(int i=1;i<=tota;++i){
		scanf("%lld",&a[i]);
		visa[a[i]]=1;
	}
	scanf("%lld",&totb);
	for(int i=1;i<=totb;++i){
		scanf("%lld",&b[i]);
		visb[b[i]]=1;
	}
	sort(v+1,v+n+1);
	for(int i=1;i<=n;++i){
		if(visa[v[i].pos]&&visb[v[i].pos]) sta1[++top1]=v[i];
		if(visa[v[i].pos]&&(!visb[v[i].pos])) sta2[++top2]=v[i];
		if((!visa[v[i].pos])&&visb[v[i].pos]) sta3[++top3]=v[i];
		if((!visa[v[i].pos])&&(!visb[v[i].pos])) sta4[++top4]=v[i];
	}
	if(n<=100000){
		for(int i=max(0,2*k-m);i<=top1;++i){
			if(i>m) break;
			int p=calc(i);
			if(p>ans) break;
			ans=min(ans,p);
		}
	}else{
		l=max(0,2*k-m),r=min(m,top1);
		if(l>r) ans=-1;
		while(r-l>2){
			int lmid=(l+r)>>1,rmid=lmid+1;
			int calcl=calc(lmid),calcr=calc(rmid);
			if(calcl>=calcr) l=lmid;
			else r=rmid;
		}
		ans=min(min(ans,calc(l)),min(calc(l+1),calc(r)));
	}
	if(ans==inf) ans=-1;
	printf("%lld\n",ans);
	return 0;
}

题:

定义f[i]表示i苹果是否一定会被吃掉,

g[i][j]表示为了不让i被吃掉是否要吃掉j

然后bitset优化转移,倒着转移方案数较少且比较方便

最后ans是否++当且仅当i和j都不一定会被吃掉且g[i]&g[j]不等于0

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<bitset>
#define re register
using namespace std;
const int MAXN=405;
const int MAXM=5e4+5;
int n,m,u[MAXM],v[MAXM],ans=0;
bool f[MAXN];
bitset<MAXN>g[MAXN];
signed main(){
	scanf("%d%d",&n,&m);
	for(re int i=1;i<=m;++i){
		scanf("%d%d",&u[i],&v[i]);
	}
	for(int i=1;i<=n;++i){
		g[i][i]=1;
		for(int j=m;j>=0;--j){
			if(g[i][u[j]]&&g[i][v[j]]){
				f[i]=1;
				break;
			}
			g[i][u[j]]=g[i][v[j]]=g[i][u[j]]|g[i][v[j]];
		}
	}
	for(int i=1;i<=n;++i){
		if(f[i]) continue;
		for(int j=i+1;j<=n;++j){
			if(f[j]) continue;
			if((g[i]&g[j])==0) ++ans;
		}
	}
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/Juve/p/11611184.html