Codeforces #848C: String Reconstruction 题解之花样虐题

显然的是,没有被赋过值的点,赋上a能保证字典序最小

不论何种方法,最终的目的是不要让一个位置被重复赋值


这题最重要的注意点是,区间可以从1e6开始,长度是1e6,所以字符串数组ans要开到2e6

另一个注意点是,对于要将所有字符串存下来做统一处理的做法,因为不确定每个字符串的长度,开的太大会mle,所以逼不得已要用string,裸的cin读入会超时

别忘了加ios::sync_with_stdio(false)


很容易想到的一个方法是将所有区间扔进数组按左端点排序

记录一个当前处理到的位置cur

如果当前区间左端点小于cur,就从cur更新到右端点(有可能cur也大于右端点,那就不更新跳过该区间)

如果左端点大于cur就老老实实更新

这样是O(2000000logn)的

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <utility>
#include <map>
#include <stack>
#include <set>
#include <vector>
#include <queue>
#include <deque>
#include <sstream>
#include <bitset>
#define x first
#define y second
#define mp make_pair
#define pb push_back
#define LL long long
#define ld long double
#define Pair pair<int,int>
#define LOWBIT(x) x & (-x)
using namespace std;

string s[100048];
int n;
Pair a[2000048];int top=0;
char ans[2000048];

int main ()
{
	int i,j,num,x,mx=-1,l;
	ios::sync_with_stdio(false);
	cin>>n;
	for (i=1;i<=n;i++)
	{
		cin>>s[i]>>num;
		l=s[i].size();
		for (j=1;j<=num;j++)
		{
			cin>>x;
			a[++top]=mp(x,i);
			mx=max(mx,x+l-1);
		}
	}
	sort(a+1,a+top+1);
	int cur=1,pp,ed;
	for (i=1;i<=top;i++)
	{
		pp=max(cur,a[i].x);
		ed=a[i].x+s[a[i].y].size()-1;
		for (j=pp;j<=ed;j++)
			ans[j]=s[a[i].y][j-a[i].x];
		cur=max(cur,ed+1);
	}
	for (i=1;i<=mx;i++) if (!isalpha(ans[i])) cout<<'a'; else cout<<ans[i];
	cout<<'\n';
	return 0;
} 

另一个很容易想到的做法是用线段树

维护区间是否已经赋过值

不过这样有点overkill

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <utility>
#include <map>
#include <stack>
#include <set>
#include <vector>
#include <queue>
#include <deque>
#include <sstream>
#include <bitset>
#define x first
#define y second
#define mp make_pair
#define pb push_back
#define LL long long
#define ld long double
#define Pair pair<int,int>
#define LOWBIT(x) x & (-x)
using namespace std;

int n,x;
char s[1000048];
char ans[2000048];

struct node
{
	int left,right;
	bool flushed;
}tree[6000048];

void build(int cur,int left,int right)
{
	tree[cur].left=left;tree[cur].right=right;tree[cur].flushed=false;
	if (left!=right)
	{
		int mid=(left+right)>>1;
		build(cur*2,left,mid);
		build(cur*2+1,mid+1,right);
	}
}

void update(int cur,int left,int right)
{
	if (tree[cur].flushed) return;
	if (tree[cur].left==tree[cur].right)
	{
		ans[tree[cur].left]=s[tree[cur].left-x];
		//cout<<tree[cur].left-x<<' '<<s[tree[cur].left-x]<<endl;
		tree[cur].flushed=true;
		return;
	}
	int mid=(tree[cur].left+tree[cur].right)>>1;
	if (left<=mid) update(cur*2,left,right);
	if (mid+1<=right) update(cur*2+1,left,right);
	if (tree[cur*2].flushed && tree[cur*2+1].flushed) tree[cur].flushed=true;
}

int main ()
{
	int i,j,num,len,mx=-1;
	scanf("%d",&n);
	build(1,1,2000000);
	for (i=1;i<=n;i++)
	{
		scanf("%s%d",s,&num);
		len=strlen(s);
		for (j=1;j<=num;j++)
		{
			scanf("%d",&x);
			mx=max(mx,x+len-1);
			update(1,x,x+len-1);
		}
	}
	for (i=1;i<=mx;i++) if (!isalpha(ans[i])) ans[i]='a';
	printf("%s",ans+1);
	printf("\n");
	return 0;
}

一个比较标准的解法是用set

开一个set来维护还没有赋值过的位置

每次读入一个l,r以后,用lower_bound来查找区间内没有被赋值的点,赋值以后从set里扔出去

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <utility>
#include <map>
#include <stack>
#include <set>
#include <vector>
#include <queue>
#include <deque>
#include <sstream>
#include <bitset>
#define x first
#define y second
#define mp make_pair
#define pb push_back
#define LL long long
#define ld long double
#define Pair pair<int,int>
#define LOWBIT(x) x & (-x)
using namespace std;

set<int> s;
set<int>::iterator iter;
char ans[2000048];
char ss[1000048];
bool visited[2000048];
int n;
int ddd[2000048],top=0;

int main ()
{
	int i,u,len,num,starter;
	scanf("%d",&n);
	//double t=GetTickCount();
	for (i=1;i<=2000010;i++) s.insert(i);
	//cout<<GetTickCount()-t<<endl;
	for (u=1;u<=n;u++)
	{
		//cout<<i<<endl;
		scanf("%s%d",ss,&num);
		//cout<<ss<<endl<<num<<endl;
		len=strlen(ss);
		for (i=1;i<=num;i++)
		{
			scanf("%d",&starter);
			iter=s.lower_bound(starter);
			top=0;
			for (;iter!=s.end() && *iter-starter+1<=len;iter++)
			{
				ans[*iter-1]=ss[*iter-starter];
				visited[*iter]=true;
				ddd[++top]=*iter;
				//cout<<u<<' '<<i<<' '<<*iter<<endl;
				
				//s.erase(*iter);
				//cout<<*iter<<endl;
				//iter++;
				//cout<<*iter<<endl;
			}
			for (int j=1;j<=top;j++) s.erase(ddd[j]);
			//puts(ans);
		}
	}
	int length=2000010;
	while (!visited[length]) length--;
	for (i=0;i<=length-1;i++) if (!isalpha(ans[i])) ans[i]='a';
	puts(ans);
	return 0;
}


相似的思路,用dsu来维护可以大幅提升效率

一个点如果已经赋过值,就合并i和i+1

这种做法很像356B Knight Tournament

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <utility>
#include <map>
#include <stack>
#include <set>
#include <vector>
#include <queue>
#include <deque>
#include <sstream>
#include <bitset> 
#define x first
#define y second
#define mp make_pair
#define pb push_back
#define LL long long
#define ld long double
#define Pair pair<int,int>
#define LOWBIT(x) x & (-x)
using namespace std;

char s[1000048];
char ans[2000048];

int pre[2000048];
inline int find_anc(int x)
{
	//cout<<"*"<<endl;
	//cout<<x<<endl;
	if (x!=pre[x]) pre[x]=find_anc(pre[x]);
	return pre[x];
}
inline void update(int x,int y)
{
	x=find_anc(x);y=find_anc(y);
	pre[x]=y;
}

int main ()
{
	int i,n,num,starter,len,mx=-1;
	for (i=1;i<=2000010;i++) pre[i]=i;
	scanf("%d",&n);
	for (int u=1;u<=n;u++)
	{
		scanf("%s%d",s,&num);
		len=strlen(s);
		for (int t=1;t<=num;t++)
		{
			scanf("%d",&starter);
			//cout<<t<<endl;
			mx=max(mx,starter+len-1);
			//cout<<t<<' '<<starter<<endl;
			for (i=find_anc(starter);i-starter+1<=len;i=find_anc(i))
			{
				//cout<<i<<endl;
				ans[i-1]=s[i-starter];
				update(i,i+1);
			}
		}
	}
	for (i=0;i<=mx-1;i++) if (!isalpha(ans[i])) ans[i]='a';
	puts(ans);
	return 0;
}

有一种十分有趣的做法

对于每个区间i,在开始处打标记i,在结束处打标记-i

把每个位置扫一遍

开一个set表示包含了当前位置的那些区间

扫到位置i时,查看所有的标记,如果是正的,将该区间扔进set,如果是负的,将该区间扔出set

如果set是空的,赋上a

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <utility>
#include <map>
#include <stack>
#include <set>
#include <vector>
#include <queue>
#include <deque>
#define x first
#define y second
#define mp make_pair
#define pb push_back
#define LL long long
#define Pair pair<int,int>
#define LOWBIT(x) x & (-x)
using namespace std;

const int INF=0x7ffffff;

int n;
string s[100048];
int len[100048];
char ans[2000048];
vector<int> a[2000048];
set<Pair> ss;

int main ()
{
	ios::sync_with_stdio(false);
	int i,j,num,x,mx=-1;
	cin>>n;
	for (i=1;i<=n;i++)
	{
		cin>>s[i]>>num;
		len[i]=s[i].size();
		for (j=1;j<=num;j++)
		{
			cin>>x;
			a[x].pb(i);
			a[x+len[i]].pb(-i);
			mx=max(mx,x+len[i]-1);
		}
	}
	for (i=1;i<=mx;i++)
	{
		for (j=0;j<a[i].size();j++)
		{
			if (a[i][j]>0) 
				ss.insert(mp(a[i][j],i)); 
			else 
				ss.erase(mp(-a[i][j],i-len[-a[i][j]]));		
		}
		if (ss.size()==0)
			ans[i]='a';
		else
		{
			Pair res=*ss.begin();
			ans[i]=s[res.x][i-res.y];
		}
	}
	puts(ans+1);
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值