Comet OJ - Contest #14

本文深入探讨了五道编程竞赛题目,包括简单的暴力求解、复杂的状态转移与区间覆盖问题,以及图论与数据结构的高级应用。文章分享了解题思路、算法优化及实现细节,适合竞赛编程爱好者学习。

正题

      我好菜啊第二题都可以调40分钟。

      Portal

      A

      这题很简单,暴力即可。

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

int n;
long long a,b,r;

int main(){
	scanf("%d %lld %lld %lld",&n,&a,&b,&r);r*=r;
	long long x,y;
	int tot=0;
	for(int i=1;i<=n;i++){
		scanf("%lld %lld",&x,&y);
		if((x-a)*(x-a)+(y-b)*(y-b)<=r) tot++;
 	}
 	printf("%d\n",tot);
}

      B

      这题好烦啊,脑子非常乱,主要的做法就是找到一个三号点看一下最前面的两个点位置在哪里,最大化[2,3]两点的距离,或者找到一个四号点,看一下最前面的三个点在哪里,或者最大化[1,2]的距离,具体自己看看吧,写麻烦了。

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

const int N=1000010;
int n,T;
char s[N];
int last[N][4],pre[N][4],ans;

int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		scanf("%s",s+1);ans=-1;
		last[0][0]=last[0][1]=last[0][2]=last[0][3]=0;
		pre[n+1][0]=pre[n+1][1]=pre[n+1][2]=pre[n+1][3]=0;
		for(int i=1;i<=n;i++){
			last[i][0]=last[i-1][0];last[i][1]=last[i-1][1];
			last[i][2]=last[i-1][2];last[i][3]=last[i-1][3];
			if(s[i]=='p' && !last[i][0]) last[i][0]=i;
			else if(s[i]=='i' && !last[i][1] && last[i][0]) last[i][1]=i;
			else if(s[i]=='n' && !last[i][2] && last[i][1]) last[i][2]=i;
			else if(s[i]=='k' && !last[i][3] && last[i][2]) last[i][3]=i;
		}
		for(int i=n;i>=1;i--){
			pre[i][0]=pre[i+1][0];pre[i][1]=pre[i+1][1];
			pre[i][2]=pre[i+1][2];pre[i][3]=pre[i+1][3];
			if(s[i]=='p' && !pre[i][0] && pre[i][1]) pre[i][0]=i;
			else if(s[i]=='i' && !pre[i][1] && pre[i][2]) pre[i][1]=i;
			else if(s[i]=='n' && !pre[i][2] && pre[i][3]) pre[i][2]=i;
			else if(s[i]=='k' && !pre[i][3]) pre[i][3]=i;
		}
		for(int i=1;i<=n;i++) 
			if(s[i]=='i' && last[i][0] && pre[i][2]) ans=max(ans,max(i-last[i][0]-1,pre[i][2]-i-1));
			else if(s[i]=='n' && last[i][1] && pre[i][3]) ans=max(ans,max(i-last[i][1]-1,pre[i][3]-i-1));
		printf("%d\n",ans);
	}
}

      C

      要是考场上没想出来就亏大了,首先我们考虑加入一个区间会使得在l这个位置上有一个开头,在r+1位置上会多出来一个开头,并且把[l,r+1]这段的所有开头都去掉,所以我们把一个区间画成一条[l,r+1]的线段和l,r+1两个黑点,那么最后的权值就是从上往下看,黑点的个数,除了第n+1位,注意,一开始就有询问[1,n]。那么一个黑点产生贡献当且仅当不选那些比它晚并且覆盖它的询问。容易发现暴力即可。

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

int n,m;
long long ans=1;
struct interval{
	int x,y;
	long long t1,t2;
}p[2010];
const long long mod=20050321;

int main(){
	scanf("%d %d",&n,&m);
	p[0].t1=1;p[0].x=1;
	long long tot=1;
	for(int i=1;i<=m;i++){
		scanf("%d %d",&p[i].x,&p[i].y);p[i].y++;
		(ans+=tot)%=mod;p[i].t1=tot;
		if(p[i].y<=n) (ans+=tot)%=mod,p[i].t2=tot;
		for(int j=0;j<i;j++){
			if(p[j].x<p[i].x || p[i].y<p[j].x) (ans+=p[j].t1)%=mod,(p[j].t1*=2)%=mod;
			if(p[j].y<p[i].x || p[i].y<p[j].y) (ans+=p[j].t2)%=mod,(p[j].t2*=2)%=mod;
		}
		(tot*=2)%=mod;
		printf("%lld\n",ans);
	}
}

      D

      首先考虑不断加入一个区间,并且把它覆盖,区间的交的和是O(n)的,考虑一次加入最多新产生两个区间就可以了。

      考虑把询问按右端点时间从小到大排序。

      我们对于一个每一个区间的交,记录一下上次覆盖这个位置是什么时候,单点加就好了,在那个时间减去原本的贡献,在这个时间加上新的贡献就好了,容易证明这个是正确的。

      可惜的就是第二题花太多时间了导致这一题晚了8分钟AC。

#include<bits/stdc++.h>
#define ls now<<1
#define rs now<<1|1
using namespace std;

const int N=500010;
struct interval{
	int x,y,c,id;
	bool operator<(const interval q)const{
		return y<q.y;
	}
}p[N],X,Y;
set<interval> S;
set<interval>::iterator it;
long long sum[N*3];
struct ques{
	int x,y,id;
	bool operator<(const ques q)const{
		return y<q.y;
	}
}q[N];
long long ans[N];
int n,m,Q;

void add(int now,int x,long long t,int l,int r){
	sum[now]+=t;
	if(l==r) return ;
	int mid=(l+r)/2;
	if(x<=mid) add(ls,x,t,l,mid);
	else add(rs,x,t,mid+1,r);
}

long long get_sum(int now,int x,int y,int l,int r){
	if(x==l && y==r) return sum[now];
	int mid=(l+r)/2;
	if(y<=mid) return get_sum(ls,x,y,l,mid);
	else if(mid<x) return get_sum(rs,x,y,mid+1,r);
	else return get_sum(ls,x,mid,l,mid)+get_sum(rs,mid+1,y,mid+1,r);
}

int main(){
	scanf("%d %d %d",&n,&m,&Q);
	for(int i=1;i<=n;i++) scanf("%d %d %d",&p[i].x,&p[i].y,&p[i].c);
	for(int i=1;i<=Q;i++) scanf("%d %d",&q[i].x,&q[i].y),q[i].id=i;
	sort(q+1,q+1+Q);
	int now=0;
	S.insert((interval){1,m,0,0});
	for(int i=1;i<=Q;i++){
		while(now<q[i].y){
			now++;
			it=S.lower_bound((interval){0,p[now].x,0,0});
			while(it!=S.end()){
				X=*it;
				if(p[now].y<X.x) break;
				if(X.x<=p[now].x && p[now].y<=X.y){
					S.erase(it);
					if(X.y>p[now].y) S.insert((interval){p[now].y+1,X.y,X.c,X.id});
					if(X.x<p[now].x) S.insert((interval){X.x,p[now].x-1,X.c,X.id});
				}
				else if(p[now].x<=X.x && X.y<=p[now].y) S.erase(it);
				else{
					S.erase(it);
					if(X.x<p[now].x) S.insert((interval){X.x,p[now].x-1,X.c,X.id});
					else S.insert((interval){p[now].y+1,X.y,X.c,X.id});
				}
				if(X.id) add(1,X.id,-1ll*X.c*(min(p[now].y,X.y)-max(p[now].x,X.x)+1),1,n);
				add(1,now,1ll*p[now].c*(min(p[now].y,X.y)-max(p[now].x,X.x)+1),1,n);
				it=S.lower_bound((interval){0,p[now].x,0,0});
			}
			S.insert((interval){p[now].x,p[now].y,p[now].c,now});
		}
		ans[q[i].id]=get_sum(1,q[i].x,q[i].y,1,n);
	}
	for(int i=1;i<=Q;i++) printf("%lld\n",ans[i]);
}

      E

      这题就很简单了,直接缩点DAG上更新答案就可以了。

      只是别忘了Tarjan要用tf标记。

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

const int N=200010,M=500010;
struct edge{
	int y,next,c;
}s[M];
int first[N],n,m,mmin[N],mmax[N],d1[N],d2[N],T,dfn[N],low[N],op=0,where[N],ans[N],in[N];
int sta[N],top=0,q,len;
bool tf[N];
queue<int> Q;
vector<edge> E[N];
void ins(int x,int y,int c){s[++len]=(edge){y,first[x],c};first[x]=len;}
void inss(int x,int y,int c){E[x].push_back((edge){y,0,c});in[y]++;}

void Tarjan(int x){
	low[x]=dfn[x]=++op;
	sta[++top]=x;tf[x]=true;
	for(int i=first[x];i!=0;i=s[i].next) {
		int y=s[i].y;
		if(!dfn[y]) Tarjan(y),low[x]=min(low[x],low[y]);
		else if(tf[y]) low[x]=min(low[x],dfn[y]);
	}
	if(low[x]==dfn[x]){
		d1[++T]=mmin[T]=1e9,d2[T]=mmax[T]=0;ans[T]=-1e9;
		while(1){
			int now=sta[top];top--;
			where[now]=T;tf[now]=false;
			if(now==x) break;
		}
	}
}

void Top_sort(){
	Q.push(where[1]);
	while(!Q.empty()){
		int x=Q.front();Q.pop();
		for(int i=0;i<E[x].size();i++){
			int y=E[x][i].y,c=E[x][i].c;
			ans[y]=max(ans[y],max(max(d2[y],c)-mmin[x],mmax[x]-min(d1[y],c)));
			ans[y]=max(ans[y],max(d2[y]-c,c-d1[y]));
			ans[y]=max(ans[y],max(ans[x],0));
			mmax[y]=max(mmax[y],max(c,mmax[x]));
			mmin[y]=min(mmin[y],min(c,mmin[x]));
			in[y]--;
			if(!in[y]) Q.push(y);
		}
	}
}

int main(){
	scanf("%d %d %d",&n,&m,&q);
	int x,y,c;ans[0]=-1e9;
	for(int i=1;i<=m;i++){scanf("%d %d %d",&x,&y,&c);ins(x,y,c);};
	Tarjan(1);
	for(x=1;x<=n;x++) if(dfn[x])
		for(int i=first[x];i!=0;i=s[i].next) 
			if(where[s[i].y]!=where[x]) inss(where[x],where[s[i].y],s[i].c);
			else d1[where[x]]=mmin[where[x]]=min(mmin[where[x]],s[i].c),d2[where[x]]=mmax[where[x]]=max(mmax[where[x]],s[i].c),ans[where[x]]=max(ans[where[x]],mmax[where[x]]-mmin[where[x]]);
	Top_sort();
	while(q--){
		scanf("%d",&x);
		if(ans[where[x]]==-1e9) printf("-1\n");
		else printf("%d\n",ans[where[x]]);
	}
}

      F

      有两种思路,一种就是按题解所说的,用一棵可持久化线段树优化建边,在开始的时候加入一个原节点,在结束的时候加入一个无用的节点就可以了,然后剩下的就是Tarjan找割点。

      我的想法就是把一条横线段看成[r,x]-[l+n,x],然后一个竖线段相当于向[x,l]-[x+n,r]的横线段连边,具体就是建两棵KDT就可以了,时间复杂度垃圾了一些O(n\sqrt n)

      没代码。

     

# YOLOv5 🚀 requirements # Usage: pip install -r requirements.txt # Base ------------------------------------------------------------------------ gitpython ipython # interactive notebook matplotlib>=3.2.2 numpy>=1.18.5 opencv-python>=4.1.1 Pillow>=7.1.2 psutil # system resources PyYAML>=5.3.1 requests>=2.23.0 scipy>=1.4.1 thop>=0.1.1 # FLOPs computation torch>=1.7.0 # see https://pytorch.org/get-started/locally (recommended) torchvision>=0.8.1 tqdm>=4.64.0 # protobuf<=3.20.1 # https://github.com/ultralytics/yolov5/issues/8012 # Logging --------------------------------------------------------------------- tensorboard>=2.4.1 # clearml>=1.2.0 # comet # Plotting -------------------------------------------------------------------- pandas>=1.1.4 seaborn>=0.11.0 # Export ---------------------------------------------------------------------- # coremltools>=6.0 # CoreML export # onnx>=1.9.0 # ONNX export # onnx-simplifier>=0.4.1 # ONNX simplifier # nvidia-pyindex # TensorRT export # nvidia-tensorrt # TensorRT export # scikit-learn<=1.1.2 # CoreML quantization # tensorflow>=2.4.1 # TF exports (-cpu, -aarch64, -macos) # tensorflowjs>=3.9.0 # TF.js export # openvino-dev # OpenVINO export # Deploy ---------------------------------------------------------------------- # tritonclient[all]~=2.24.0 # Extras ---------------------------------------------------------------------- # mss # screenshots # albumentations>=1.0.3 # pycocotools>=2.0 # COCO mAP # roboflow # ultralytics # HUB https://hub.ultralytics.com
05-25
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值