我是靠谱客的博主 微笑香水,最近开发中收集的这篇文章主要介绍2021 ICPC Asia Taipei Regional Programming Contest C、F,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

C、Community Service

题意:
有一个 0 到 1 e 6 0到1e6 01e6的数轴,有 2 e 5 2e5 2e5次操作:
操作一:增加一条 从 l 到 r l到r lr的线段,每条线段有名字;
操作二:给定一个区间 S S S 以及范围 l 到 r l到r lr,求和 S S S有交集的 最近一个被加入的线段 T T T,并将 T T T从线段集中删去。

思路1:
如果没有删除线段的操作,那么这道题就是 线段树区间赋值维护最大值,并且区间求最大值。
但是有删除操作之后,线段之间的关系就是相互覆盖,很容易想到 可持久化相关的数据结构。我的想法是 用主席树来维护增加线段,那么求最近的有交集的线段 就可以用 二分+主席树区间求和来实现。但是对于删除线段的操作,就需要在主席树上再套个数据结构来维护了,且不说时间复杂度会不会爆炸,感觉实现的难度就很大,况且也不确定 树套树能不能维护。如果有知道的,可以告诉我。

思路2:
首先,考虑有两颗 set 的线段树,对于增加一个线段的操作,我们对第一颗线段树,只在 l 到 r l到r lr 分成的 l o g log log个区间的set中 加入当前线段的编号;对第二颗线段树,我们在 所有 l o g log log个区间,以及这些区间到根区间的set 都加入当前线段的编号。
考虑询问的时候,第一个线段树,对于 l 到 r l到r lr分成的log个线段,我们对所有这些区间 以及到根区间的 set 取 max,也及覆盖询问区间的log个段 的线段的最大编号;第二个线段树,只对于 l o g log log 个段的set最大值 取max,也及询问区间的 l o g log log个段 覆盖的线段的最大编号。这样时间复杂度 就是严格的线段树 O ( l o g n ) O(logn) O(logn),以及 set O ( l o g n ) O(logn) O(logn)
对于删除操作,就是插入的反操作,把标号从set中删除即可。

然后兴高采烈地写完,你会发现 T 54 T54 T54,考虑优化时间复杂度。
注意到,上面的思路中,set的作用就是起到 存储一个序列,并且支持 查询最大值 以及删除一个数的功能。同时,我们插入的编号是 有递增性质的。那么vector或者stack都能实现,存储序列以及查询最大值的操作,但是对于删除一个数,我们可以不用立即删,可以对编号打个标记,下次取出来的手再删即可。

详情见代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,n,m) for(int i=n;i<=m;i++)
#define repp(i,n,m) for(int i=n;i>=m;i--)
typedef pair<int,int> pii;

const ll mod=998244353;
const ll maxn=1e7+5;
const ll INF=0x3f3f3f3f;
ll qp(ll x,ll y){
	ll ans=1;
	while(y){
		if(y%2)ans=ans*x%mod;x=x*x%mod;y/=2;}return ans;}
ll fmul(ll x,ll y){ 	//蹇€熶箻
    ll tmp=(x*y-(ll)((long double)x/mod*y+1.0e-8)*mod);
	return tmp<0?tmp+mod:tmp; 
}    
inline int qread(){
    int s=0,w=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')w=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
    return (w==-1?-s:s);}

int n,m;
vector<int>tr[4000060],tr1[4000060];
int vis[200060];
int cnt=0,ans;
struct no{
	char s[20];
	int l,r;
}z[200050];
void update(int p,int l,int r,int x,int y,int w){
	tr[p].push_back(w);
	if(x<=l&&r<=y){
		return;
	}
	int mid=l+r>>1;
	if(x<=mid)update(2*p,l,mid,x,y,w);
	if(mid<y)update(2*p+1,mid+1,r,x,y,w);
}
void update1(int p,int l,int r,int x,int y,int w){
	if(x<=l&&r<=y){
		tr1[p].push_back(w);
		return;
	}
	int mid=l+r>>1;
	if(x<=mid)update1(2*p,l,mid,x,y,w);
	if(mid<y)update1(2*p+1,mid+1,r,x,y,w);
}
void query(int p,int l,int r,int x,int y){
	if(x<=l&&r<=y){
		while(tr[p].size()){
			int now=*(tr[p].end()-1);
			if(vis[now]){
				tr[p].erase(tr[p].end()-1);
				continue;
			}
			ans=max(ans,now);
			break;
		}
		return;
	}
	int mid=l+r>>1;
	if(x<=mid)query(2*p,l,mid,x,y);
	if(mid<y)query(2*p+1,mid+1,r,x,y); 
}
void query1(int p,int l,int r,int x,int y){
	while(tr1[p].size()){
		int now=*(tr1[p].end()-1);
		if(vis[now]){
			tr1[p].erase(tr1[p].end()-1);
			continue;
		}
		ans=max(ans,now);
		break;
	}
	if(x<=l&&r<=y)return;
	int mid=l+r>>1;
	if(x<=mid)query1(2*p,l,mid,x,y);
	if(mid<y)query1(2*p+1,mid+1,r,x,y); 
}
int main(){
	n=qread();m=qread();
	while(m--){
		int op;
		op=qread();
		if(op==1){
			cnt++;
			scanf("%s",z[cnt].s);
			z[cnt].l=qread();z[cnt].r=qread();
			update(1,0,n-1,z[cnt].l,z[cnt].r,cnt);
			update1(1,0,n-1,z[cnt].l,z[cnt].r,cnt);
		}else{
			ans=0;
			int l=qread(),r=qread();
			query(1,0,n-1,l,r);
			query1(1,0,n-1,l,r);
			if(ans==0){
				printf(">_<n");
			}else {
				printf("%sn",z[ans].s);
				vis[ans]=1;
			}
		}
	}
	return 0;
}

F. What a Colorful Wall

题意:
在 一个 2 e 8 ∗ 2 e 8 2e8*2e8 2e82e8的二维平面上,执行 4000 4000 4000次操作,每次操作是把一个矩形涂成一个给定元素。

思路:
这是一道比较经典的题目,就是用并查集维护,还没涂的点。
考虑倒序涂矩形,那么就可以用并查集维护下一个还没涂的点。
有一个细节是,对于每个 y y y都需要所有的 x x x,也及总共 8000 ∗ 8000 8000*8000 80008000个点。少了的话,会有问题,可以画图找一下。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,n,m) for(int i=n;i<=m;i++)
#define repp(i,n,m) for(int i=n;i>=m;i--)
typedef pair<int,int> pii;

const ll mod=998244353;
const ll maxn=1e7+5;
const ll INF=0x3f3f3f3f;
ll qp(ll x,ll y){
	ll ans=1;
	while(y){
		if(y%2)ans=ans*x%mod;x=x*x%mod;y/=2;}return ans;}
ll fmul(ll x,ll y){ 	//蹇€熶箻
    ll tmp=(x*y-(ll)((long double)x/mod*y+1.0e-8)*mod);
	return tmp<0?tmp+mod:tmp; 
}    
inline ll qread(){
    ll s=0,w=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')w=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
    return (w==-1?-s:s);}

int n;
struct node{
	int a,b,c,d;
	int co;
}z[40060];
unordered_map<int,int>vis,idx;
struct no{
	int x,y;
}zz[64008001];
int ans=0;
int fa[64008001];
bool cmp(no a,no b){
	if(a.y==b.y)return a.x<b.x;
	return a.y<b.y;
}

int id=0;
int find(int x){
	if(x==fa[x])return x;
	return fa[x]=find(fa[x]);
}
int vv[4060];
vector<int>vx,vy;
int tot=0;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d%d%d%d",&z[i].a,&z[i].b,&z[i].c,&z[i].d,&z[i].co);
		vx.push_back(z[i].a);
		vx.push_back(z[i].c);
		vy.push_back(z[i].b);
		vy.push_back(z[i].d);
	}
	sort(vx.begin(),vx.end());
	vx.erase(unique(vx.begin(),vx.end()),vx.end());
	sort(vy.begin(),vy.end());
	vy.erase(unique(vy.begin(),vy.end()),vy.end());
	for(int i=0;i<vy.size();i++){
		vis[vy[i]]=++id;
		for(int j=0;j<vx.size();j++){
			tot++;
			fa[tot]=tot;
			zz[tot].x=vx[j];
			zz[tot].y=vy[i];
		}
		tot++;
		fa[tot]=tot;
		zz[tot].x=1e9;
		zz[tot].y=vy[i];
	}
	for(int j=0;j<vx.size();j++){
		idx[vx[j]]=j+1;
	}
	for(int i=n;i>=1;i--){
		int f=0;
		for(int j=vis[z[i].d];j<vis[z[i].b];j++){
			int pos=(j-1)*(vx.size()+1)+idx[z[i].a];
			pos=find(pos);
			for(int k=pos;zz[k].x<z[i].c;k=find(k)){
				fa[k]=k+1;
				f=1;
			}
		}
		if(f&&!vv[z[i].co]){
			vv[z[i].co]=1;
			ans++;
		}
	}
	printf("%dn",ans);
	return 0;
}

最后

以上就是微笑香水为你收集整理的2021 ICPC Asia Taipei Regional Programming Contest C、F的全部内容,希望文章能够帮你解决2021 ICPC Asia Taipei Regional Programming Contest C、F所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(47)

评论列表共有 0 条评论

立即
投稿
返回
顶部