Codeforces Round #603 Div2

1263E Editor(线段树维护括号序列)

题意:

​ 文本初始为空,给定长度为n的操作序列对文本进行编辑,每次操作后判断文本的括号序列合法性并求最大嵌套层数 n 1 0 6 (n\le10^6)

解法:

​ ‘(’ 记为1,’)’ 记为-1,其他字符记为0。若所得序列每一个前缀和均不为负,说明在任意前缀字符串中左括号数量多于右括号;若整个序列的和为0,则左括号总数等于右括号,此时括号序列合法。最大嵌套层数等于序列所有前缀和中的最大值。因此用线段树维护区间和、区间前缀最大值与最小值。

​ 故利用单点修改、只需查询根节点的线段树。维护区间前缀最值时注意线段树区间合并的性质,有: { t r e e [ r t ] . m x v = m a x ( t r e e [ l s o n ] . m x v , t r e e [ r s o n ] . m x v + t r e e [ l s o n ] . s u m ) t r e e [ r t ] . m n v = m i n ( t r e e [ l s o n ] . m n v , t r e e [ r s o n ] . m n v + t r e e [ l s o n ] . s u m ) \begin {cases} tree[rt].mxv=max(tree[lson].mxv,tree[rson].mxv+tree[lson].sum)\\tree[rt].mnv=min(tree[lson].mnv,tree[rson].mnv+tree[lson].sum) \end{cases}

​ 复杂度: O ( n l o g n ) O(nlogn)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
char s[maxn],txt[maxn];
int id[128];
struct tree
{	int mxv,mnv,sum; }t[maxn<<2];
void update(int rt,int pos,int l,int r,int val)
{
	if(l==r)
	{	t[rt].mxv=val,t[rt].mnv=val,t[rt].sum=val; return; }
	int lson=rt<<1,rson=lson|1,mid=(l+r)>>1;
	if(pos<=mid) update(lson,pos,l,mid,val);
	if(pos>mid) update(rson,pos,mid+1,r,val);
	t[rt].mxv=max(t[lson].mxv,t[lson].sum+t[rson].mxv);
	t[rt].mnv=min(t[lson].mnv,t[lson].sum+t[rson].mnv);
	t[rt].sum=t[lson].sum+t[rson].sum;
}
int main()
{
	int n,pt=1;
	id['(']=1,id[')']=-1;
	cin>>n>>s;
	for(int i=0;i<n;i++)
	{
		if(s[i]=='R') pt++;
		else if(s[i]=='L')
		{	if(pt>1) pt--; } 
		else update(1,pt,1,n,id[(int)s[i]]);
		if(t[1].sum==0&&t[1].mnv>=0) printf("%d ",t[1].mxv);
		else printf("-1 ");
	}
}

1263F Economic Difficulties(DFS DP)

题意:

​ 有主副两个电网和n个电子设备,两个电网分别形成一棵恰有n个叶节点的树,两棵树的每个叶节点与一个设备相连且每个设备仅分别与两棵树的一个叶节点相连。并且满足:将n个设备编号为1、2、……、n后,形成的两棵树任意两条边不交叉。现可删掉一些电线(边),在仅对两电网的根节点供电的条件下,使得每个电子设备仍能正常工作(有边与任意叶节点相连),求最大的删边数 n 1 0 3 (n\le10^3)

解法:

​ 显然对每个电子设备而言只需保留一条边,即必有一条边被删去。又由于两棵树满足上述特殊的性质,可以发现任意一颗子树包含的所有叶节点所对应的设备编号一定是连续的。因此可通过DFS预处理出考虑编号在l到r之间设备的最大删边数 m x v [ l ] [ r ] mxv[l][r] :对树上每个结点 u u 维护 l b d [ u ] r b d [ u ] s i z [ u ] lbd[u]、rbd[u]、siz[u] ,分别表示以u为根的子树叶节点对应设备编号的左边界右边界子树大小,从而有 m x v [ l b d [ u ] ] [ r b d [ u ] ] ] = m a x ( s i z [ u ] ) mxv[lbd[u]][rbd[u]]]=max(siz[u])

​ 进而考虑DP,设 d p [ i ] dp[i] 为仅对前i个设备进行删边的答案,则转移方程如下:

d p [ i ] = m a x ( d p [ j 1 ] + m x v [ j ] [ i ] ) , 1 j i dp[i]=max(dp[j-1]+mxv[j][i]),1\le j\le i

​ 复杂度: O ( n 2 ) O(n^2)

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e3+5,inf=0x3f3f3f3f;
vector<int>e[maxn];
int lbd[maxn],rbd[maxn],siz[maxn];
int mxv[maxn>>1][maxn>>1],dp[maxn>>1];
void dfs(int u)
{
	siz[u]=u>1;
	for(int v:e[u])
	{
		dfs(v),siz[u]+=siz[v];
		lbd[u]=min(lbd[u],lbd[v]);
		rbd[u]=max(rbd[u],rbd[v]);
	}
	int &ans=mxv[lbd[u]][rbd[u]];
	ans=max(ans,siz[u]);
}
int main()
{
	int n,m,x,t=2;
	scanf("%d",&n);
	while(t--)
	{
		scanf("%d",&m);
		for(int i=1;i<=m;i++)
			e[i].clear(),lbd[i]=n,rbd[i]=1;
		for(int i=1;i<m;i++)
			scanf("%d",&x),e[x].push_back(i+1);
		for(int i=1;i<=n;i++)
			scanf("%d",&x),lbd[x]=rbd[x]=i;
		dfs(1);
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=i;j++)
			dp[i]=max(dp[i],dp[j-1]+mxv[j][i]);
	printf("%d",dp[n]);
}
发布了5 篇原创文章 · 获赞 3 · 访问量 129

猜你喜欢

转载自blog.csdn.net/weixin_43899905/article/details/103478829
今日推荐