2020-2021 ICPC, NERC, Southern and Volga Russian Regional Contest

2020-2021 ICPC, NERC, Southern and Volga Russian Regional Contest

Solved:
C、Berpizza
D、Firecrackers
E、Four Segments
F、Full Turn
H、K and Medians
J、Road Reform
K、The Robot
N、Waste Sorting


C、Berpizza

两个优先队列模拟,一个优先队列以编号(进入顺序)为关键字,另一个以权值为关键字,用一个vis数组标记某个编号是否已经出去,模拟即可。

		struct node1{
			int id;
			int cost;
			bool operator < (node1 a) const {
				return id>a.id;
			}
		};
		 
		struct node2{
			int id;
			int cost;
			bool operator < (node2 a) const {
				if(cost==a.cost){
					return id>a.id;
				}else{
					return cost<a.cost;
				}
			}
		};
		priority_queue<node1>q1;
		priority_queue<node2>q2;
		int vis[500005];
		int main()
		{
			int T,now=0;
			cin>>T;
			while(T--){
				int pos;
				scanf("%d",&pos);
				if(pos==1){
					++now;
					node1 x;
					node2 y;
					int cost;
					scanf("%d",&cost);
					x.id=now;
					x.cost=cost;
					y.id=now;
					y.cost=cost;
					q1.push(x);
					q2.push(y);
				}else if(pos==2){
					int nowpos;
					while(1){
						nowpos=q1.top().id;
						q1.pop();
						if(!vis[nowpos]){
							vis[nowpos]=1;
							break;
						}				
					}
					printf("%d ",nowpos);
				}else if(pos==3){
					int nowpos;
					while(1){
						nowpos=q2.top().id;
						q2.pop();
						if(!vis[nowpos]){
							vis[nowpos]=1;
							break;
						}				
					}
					printf("%d ",nowpos);
				}
			}
			return 0;
		} 

D、Firecrackers

最多可以放置的爆竹个数必定是警察到小偷的距离-1。为了在被抓之前看到爆竹爆炸,一定是选择最小的几个放置。首先选择爆炸时间前k个,k=最多可以放置的爆竹个数,最后求出在小偷不被抓的情况下,每次放爆竹时尽量选择大的且可以在规定时间内爆炸的即可。

		int s[200010];
		int ss[200010];
		int main()
		{
			int T;
			scanf("%d",&T);
			while(T--){
				mem(s,0);
				ll n,m,a,b;
				scanf("%lld%lld%lld%lld",&n,&m,&a,&b);
				for(int i=1;i<=m;i++)
				{
					scanf("%d",&s[i]);
				}
				sort(s+1,s+m+1);
				int xx = min(abs(b - a)- 1,m);
				if(a<b)
				{
					for(int i=1;i<=xx;i++)
					{
						ss[i] = b - 1 - s[i] - 1;
					}
					sort(ss+1,ss+1+xx);
					int ans = 0;
					int q = 0;
					for(int i=1;i<=xx;i++)
					{
						if(ans<=ss[i])
						{
							ans++;
							q++;
						}
					}
					cout<<q<<endl; 
				}
				else{
					a = n-a+1;
					b = n-b+1;
					for(int i=1;i<=xx;i++)
					{
						ss[i] = b - 1 - s[i] - 1;
					}
					sort(ss+1,ss+1+xx);
					int ans = 0;
					int q = 0;
					for(int i=1;i<=xx;i++)
					{
						if(ans<=ss[i])
						{
							ans++;
							q++;
						}
					}
					cout<<q<<endl;
				}
			}
		}

E. Four Segments

第一小的和第三小的相乘即是本题答案。

		int main()
		{
			int T;
			cin>>T;
			while(T--){
				int a[6];
				for(int i=1;i<=4;i++){
					scanf("%d",&a[i]);
					
				}
				sort(a+1,a+5);
				printf("%d\n",a[1]*a[3]);
			}
		 } 

F. Full Turn

题意:给你2n对点,每组两对点,表示一个人在点(x1,y1) 处且注视着(x2,y2)处,当每个人同时按相同方向旋转360度,问有多少对人可以相互注视到。
思路:把每个人和其注视点化成一个向量,此时可以发现,当两个人可以注视到时,两个向量方向相反。即可以表示为 (ax,xy)=(-bx,-by)。因为此时只关注方向而忽略大小,因此通过求gcd可以把每个向量化到最简,然后用map存储每个方向拥有的向量个数,最后统计相反方向的向量个数即可。

		map<pair<ll,ll>,int>mp;
		vector<pair<ll,ll> >v;
		int main()
		{
			int T;
			scanf("%d",&T);
			while(T--){
				v.clear();
				mp.clear();
				int n;
				scanf("%d",&n);
				for(int i=1;i<=n;i++){
					ll x1,y1,x2,y2;
					scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
					ll x,y;
					x=x2-x1;
					y=y2-y1;
					ll num=__gcd(abs(x),abs(y));
					x=x/num;
					y=y/num;
					if(mp[make_pair(x,y)]>0){
						mp[make_pair(x,y)]++;
					}else{
						mp[make_pair(x,y)]=1;
						v.push_back(make_pair(x,y));
					}
				}
				ll sum=0;
				for(int i=0;i<v.size();i++){
					ll nowx=v[i].first,nowy=v[i].second;
					//cout<<nowx<<" "<<nowy<<"xx"<<endl;
					sum+=1ll*mp[make_pair(nowx,nowy)]*mp[make_pair(-nowx,-nowy)];
				}
				sum=sum/2;
				printf("%lld\n",sum);
			}
		}

H. K and Medians

题意:给你1~n的数组a,再给你数组p,每次你可以在a数组中选择 k个数字(k一定是奇数),删掉这k个数字除了中位数外的数字,问能否将a数组变成p数组。
思路:首先将所有要删除的数筛出来。这些数需要满足,1、总数%(k-1)==0,接下来看,我们需要2、有一个不能删除的点作为中位数的点,然后去删除那些需要删除的数,最后要考虑这个点的位置,3、点的位置后需要删除的数不能少于k/2个

		int vis[200005];
		int a[200005];
		void solve()
		{
		    int n,k,m;
		    mem(vis,0);
		    scanf("%d%d%d",&n,&k,&m);
		    for(int i=1;i<=m;i++){
		        int num;
		        scanf("%d",&num);
		        vis[num]=1;
		    }
		    int pos=0;
		    for(int i=1;i<=n;i++){
		        if(!vis[i]){
		            a[++pos]=i;
		        }
		    }
		    for(int i=k/2+1;i<=pos;i++){
		        //cout<<pos-i<<endl;
		        if(pos%(k-1)==0&&pos-i+1>=k/2&&a[i]!=a[i-1]+1){
		            cout<<"YES"<<endl;
		            return ;
		        }
		    }
		    cout<<"NO"<<endl;
		    return ;
		}
		 
		int main()
		{
		    int T;
		    cin>>T;
		    while(T--){
		        solve();
		    }
		    return 0;
		}

J. Road Reform

题意:给出一幅图,通过删边把这幅图变成一棵生成树,要让这颗树上最大边的权值变成k,每步操作可以把某一条边的权值+1或-1,问你最少需要操作几次。
思路:1、首先考虑所有比k小的边就可以构成一棵树,那么只需要找到最接近k的边,加入这棵树,然后让其他小于k的边去构成树。2、如果比k小的边只能连接一部分点,那么我们的目的就是让比k的边尽量少且小,那么考虑最小生成树,即把由比k小的边连接的所有点当作一个大点,其他点独立,通过最小生成树算法去跳出那些比k大的边即可。

		struct node{
			ll u,v,w;
		}s[200005],ss[200005];
		bool cmp(node a,node b){
			return a.w<b.w;
		}
		ll fa[200005];
		ll vis[200005];
		ll find(ll x)
		{
		    return fa[x]==x?x:fa[x]=find(fa[x]);
		}
		 
		void merge(ll x,ll y)
		{
		    x=find(x);
		    y=find(y);
		    fa[x]=y;
		}
		int main()
		{
			ll T,n,m,k;
			cin>>T;
			while(T--){
				memset(vis,0,sizeof vis);
				scanf("%lld%lld%lld",&n,&m,&k);
		         for(int i=1;i<=n;i++){
		             fa[i]=i;
		         }
				for(int i=1;i<=m;i++){
					ll u,v,w;
					scanf("%lld%lld%lld",&u,&v,&w);
					s[i].u=u;
					s[i].v=v;
					s[i].w=w;
				}
				sort(s+1,s+m+1,cmp);
		        int rpos=m;
				for(int i=1;i<=m;i++){
		            if(s[i].w<=k){
		                merge(s[i].u,s[i].v);
		            }else{
		                rpos=i-1;
		                break;
		            }
		        }
		        int kk=1;
		        for(int i=1;i<n;i++){
		            if(find(i)!=find(i+1)){
		                kk=0;
		                break;
		            }
		        }
		        if(kk){
		            if(rpos+1<=m){
		                printf("%lld\n",min(abs(s[rpos].w-k),abs(s[rpos+1].w-k)));
		            }else{
		                printf("%lld\n",abs(s[rpos].w-k));   
		            }
		            
		        }else{
		            int now=0;
		            ll sum=0;
		            for(int i=1;i<=m;i++){
		                if(s[i].w>k){
		                    ss[++now].u=s[i].u;
		                    ss[now].v=s[i].v;
		                    ss[now].w=s[i].w-k;
		                }
		            }
		            sort(ss+1,ss+now+1,cmp);
		            for(int i=1;i<=now;i++){
		                int ufa=find(ss[i].u),vfa=find(ss[i].v);
		                if(ufa!=vfa){
		                    merge(ss[i].u,ss[i].v);
		                    sum+=ss[i].w;
		                }
		            }
		            printf("%lld\n",sum);
		        }
			}
		}

K. The Robot

发现障碍必定是放在机器人要经过的路上,通过求机器人的路径,对每个点暴力搜索即可。

		string s;
		map<pair<int,int>,int>vis;
		bool bfs(int x,int y,int step)
		{
			int nowx = x; 
			int nowy = y;
			if(s[step]=='L')
			{
				nowx++;
			}
			else if(s[step]=='R')
			{
				nowx--;
			}
			else if(s[step]=='U')
			{
				nowy--;
			}
			else{
				nowy++;
			}
			for(int i=step+1;i<s.length();i++)
			{
				if(s[i]=='U')
				{
					nowy++;
				}
				else if(s[i]=='D')
				{
					nowy--;
				}
				else if(s[i]=='R')
				{
					nowx++;
				}
				else{
					nowx--;
				}
				if(nowx==x&&nowy==y)
				{
					if(s[i]=='L')
		        	{
			        	nowx++;
		         	}
		        	else if(s[i]=='R')
		        	{
		    	    	nowx--;
		        	}
		         	else if(s[i]=='U')
		        	{
			         	nowy--;
		        	}
		        	else{
		    	    	nowy++;
		        	}    
				}
			}
			//cout<<nowx<<" "<<nowy<<endl;
			if(nowx==0&&nowy==0)
			{
				return true;
			 } 
			 else {
			 	return false;
			 }
		}
		int main()
		{
			int t;
			scanf("%d",&t);
			while(t--)
			{
				bool flag = 0;
				vis.clear();
				vis[make_pair(0,0)] = 1;
				cin>>s;
				int xx = 0;
				int yy = 0;
				for(int i=0;i<s.length();i++)
				{
					if(s[i]=='U')
					{
						yy++;
					}
					else if(s[i]=='D')
					{
						yy--;
					}
					else if(s[i]=='R')
					{
						xx++;
					}
					else{
						xx--;
					}
					//cout<<xx<<yy<<endl;
					if(!vis[make_pair(xx,yy)])
					{
						if(bfs(xx,yy,i))
						{
							printf("%d %d\n",xx,yy);
							flag = 1; 
							break;
						}
						vis[make_pair(xx,yy)] = 1;
					}
				}
				if(!flag)
				{
					puts("0 0");
				}
			}
		}

N. Waste Sorting

阅读理解。

		string s;
		int main()
		{
			int T;
			cin>>T;
			while(T--){
				int c1,c2,c3;
				int a1,a2,a3,a4,a5;
				scanf("%d%d%d %d%d%d%d%d",&c1,&c2,&c3,&a1,&a2,&a3,&a4,&a5);
				if(c1<a1||c2<a2) 
				{
					puts("NO");
					continue;
				 } 
				c1=c1-a1-a4;
				c2=c2-a2-a5;
				if(c1<0&&c2<0)
				{
					if(c3>=abs(c1+c2)+a3) puts("YES");
					else puts("NO");
				}
				else if(c1>=0&&c2>=0)
				{
					if(c3>=a3) puts("YES");
					else puts("NO");
				}
				else if(c1<=0&&c2>=0)
				{
					if(c3>=abs(c1)+a3) puts("YES");
					else puts("NO");
				}
				else if(c1>=0&&c2<=0)
				{
					if(c3>=abs(c2)+a3) puts("YES");
					else puts("NO");
				}
				else 
				{
					puts("NO");
				}
			}
		 } 
posted @ 2021-01-18 23:01  hachuochuo  阅读(172)  评论(0编辑  收藏  举报