Codeforces Round #603 (Div. 2) 题解

121 阅读3分钟

比赛链接

E

思路

mm为打印出来的字符串的最大长度。设一个数组bb,如果有一个位于ii的左括号,就将b[i]b[m]b[i]\sim b[m]的数值+1+1,与之相反地,右括号就1-1。只要b[i]b[i]的最小值非负且b[n]=0b[n]=0,这个字符串就合法,最大嵌套深度(即需要的颜色数)就是max(b[i])max(b[i])

用线段树维护bb数组,复杂度为O(nlogm)O(n\log m),这里的mm不要取太大,大致取输入中RR的个数加一个常数即可。

代码

#include<bits/stdc++.h>
#define lc p<<1
#define rc p<<1|1
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define bl(u,i) for(int i=head[u];i;i=e[i].nxt)
#define en puts("")
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll> 
typedef int ll;
typedef double db;
using namespace std;
const ll INF=0x3f3f3f3f;
void read() {}
void OP() {}
void op() {}
template <typename T, typename... T2>
inline void read(T &_, T2 &... oth)
{
    int __=0;
    _=0;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            __=1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        _=_*10+ch-48;
        ch=getchar();
    }
    _=__?-_:_;
    read(oth...);
}
template <typename T>
void Out(T _)
{
    if(_<0)
    {
        putchar('-');
        _=-_;
    }
    if(_>=10)
       Out(_/10);
    putchar(_%10+'0');
}
template <typename T, typename... T2>
inline void OP(T _, T2... oth)
{
	Out(_);
	putchar('\n');
	OP(oth...);
}
template <typename T, typename... T2>
inline void op(T _, T2... oth)
{
	Out(_);
	putchar(' ');
	op(oth...);
}
/*#################################*/
const ll N=1E6+10;
ll n;
ll sum[4*N],mini[4*N],maxi[4*N],tag[4*N];
string s;
ll check(char ch)
{
	return ch=='(' || ch==')';
}
void flag(ll p,ll l,ll r,ll val)
{
	tag[p]+=val;
	sum[p]+=(r-l+1)*val;
	mini[p]+=val;
	maxi[p]+=val;
}
void update(ll p)
{
	sum[p]=sum[lc]+sum[rc];
	mini[p]=min(mini[lc],mini[rc]);
	maxi[p]=max(maxi[lc],maxi[rc]);
}
void push_down(ll p,ll l,ll r)
{
	ll mid=(l+r)>>1;
	flag(lc,l,mid,tag[p]);
	flag(rc,mid+1,r,tag[p]);
	tag[p]=0;
}
void add(ll p,ll l,ll r,ll al,ll ar,ll val)
{
	if(l>=al && r<=ar)
	{
		sum[p]+=(r-l+1)*val;
		tag[p]+=val;
		mini[p]+=val;
		maxi[p]+=val;
		return;
	}
	push_down(p,l,r);
	ll mid=(l+r)>>1;
	if(al<=mid)
		add(lc,l,mid,al,ar,val);
	if(ar>mid)
		add(rc,mid+1,r,al,ar,val);
	update(p);
}
ll SUM(ll p,ll l,ll r)
{
	if(l==r)
		return sum[p];
	push_down(p,l,r);
	ll mid=(l+r)>>1;
	return SUM(rc,mid+1,r);
}
ll MIN(ll p,ll l,ll r,ll al,ll ar)
{
	if(l>=al && r<=ar)
		return mini[p];
	push_down(p,l,r);
	ll mid=(l+r)>>1;
	ll ret=INT_MAX;
	if(al<=mid)
		ret=min(ret,MIN(lc,l,mid,al,ar));
	if(ar>mid)
		ret=min(ret,MIN(rc,mid+1,r,al,ar));
	return ret;
}
ll MAX(ll p,ll l,ll r,ll al,ll ar)
{
	if(l>=al && r<=ar)
		return maxi[p];
	push_down(p,l,r);
	ll mid=(l+r)>>1;
	ll ret=0;
	if(al<=mid)
		ret=max(ret,MAX(lc,l,mid,al,ar));
	if(ar>mid)
		ret=max(ret,MAX(rc,mid+1,r,al,ar));
	return ret;
}
char cur[N];
int main()
{
	// cout<<find(" (ab)");
	ll n;
	string s;
	read(n);
	cin>>s;
	s=' '+s;
	ll m=10;
	rep(i,1,n)
		if(s[i]=='R')
			++m;
	ll ind=1;
	ll ans=0;
	rep(i,1,n)
	{
		if(s[i]=='L' || s[i]=='R')
		{
			if(s[i]=='L' && ind>1)
				--ind;
			else if(s[i]=='R')
				++ind;
			op(ans);
			continue;
		}
		if(check(cur[ind]))
		{
			if(cur[ind]=='(')
				add(1,1,m,ind,m,-1);
			else
				add(1,1,m,ind,m,1);
		}
		if(s[i]=='(')
			add(1,1,m,ind,m,1);
		else if(s[i]==')')
			add(1,1,m,ind,m,-1);
		cur[ind]=s[i];
		if(SUM(1,1,m)!=0 || MIN(1,1,m,1,m)<0)
		{
			op(ans=-1);
			continue;
		}
		op(ans=MAX(1,1,m,1,m));
	}
}

F

思路

贪心肯定不行,可以自己举一下反例。

每个设备最终肯定只被一棵树供电,设dp[i]dp[i]为保证前ii个设备供电时最多可以删除多少条边。计算dp[i]dp[i]时,ii前面连续的一段必然是由同一颗树供电,设f[0/1][l][r]f[0/1][l][r]为上边/下边的树不给lrl\sim r供电最多可以删多少边,因此推出转移方程dp[i]=max(dp[j]+f[0/1][j+1][i])dp[i]=max(dp[j]+f[0/1][j+1][i])

怎么求ff数组呢?考虑对于每个树上的节点uu,计算出它们能到达的最靠左的叶节点L[u]L[u]和最靠右的叶节点R[u]R[u]。当删除uu后,L[u]R[u]L[u]\sim R[u]的叶节点都被切断供电(因为树上不存在交叉的边),这时候删除的边数就是size[u]size[u],即以uu为根子树的大小(因为每个节点都仅有一条边连接它的父亲,如果u=1u=1的话,因为11没有父节点,删除边数就是size[u]1size[u]-1)。

这样求出来的ff数组并非完全符合它的定义,如果存在某个区间需要删除超过一个节点才可以全部断电,那么这个区间对应的答案并不会维护到ff中。因此f[0/1][l][r]f[0/1][l][r]实际上存储的是在树上仅删除一个节点,使得lrl\sim r的设备全部断电时最多能删除多少边。但我们用这个ff数组去DP是可以得到正确答案的,因为对于没有维护进ff的区间(假设这个区间需要删两个节点才能全部覆盖,据此将其分为l1r1,l2r2l_1\sim r_1,l_2\sim r_2,并且这两个区间必然首尾相接),在求dp[r2]dp[r_2]时必然会用到dp[r1]dp[r_1]的答案,而dp[r1]dp[r_1]已经考虑了第一个区间的影响,只需要在此基础上将f[0/1][l2][r2]f[0/1][l_2][r_2]的影响也考虑进去即可。所以这个大的区间被拆成两次维护进dpdp中,保证了正确性。

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define bl(u,i) for(int i=head[u];i;i=e[i].nxt)
#define en puts("")
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll> 
typedef long long ll;
typedef double db;
using namespace std;
const ll INF=0x3f3f3f3f;
void read() {}
void OP() {}
void op() {}
template <typename T, typename... T2>
inline void read(T &_, T2 &... oth)
{
    int __=0;
    _=0;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            __=1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        _=_*10+ch-48;
        ch=getchar();
    }
    _=__?-_:_;
    read(oth...);
}
template <typename T>
void Out(T _)
{
    if(_<0)
    {
        putchar('-');
        _=-_;
    }
    if(_>=10)
       Out(_/10);
    putchar(_%10+'0');
}
template <typename T, typename... T2>
inline void OP(T _, T2... oth)
{
	Out(_);
	putchar('\n');
	OP(oth...);
}
template <typename T, typename... T2>
inline void op(T _, T2... oth)
{
	Out(_);
	putchar(' ');
	op(oth...);
}
/*#################################*/
using namespace std;
const ll N=2E3+10;
ll n,a,pi,tot;
ll head[N],siz[N],L[2][N],R[2][N],f[2][N][N],dp[N];
struct Edge{
	ll nxt,to,w;
}e[N];
void add_edge(ll u,ll v,ll w,int flag)
{
	e[++tot]=(Edge){head[u],v,w};
	head[u]=tot;
	if(flag)
		add_edge(v,u,w,0);
}
void dfs(ll u,ll tree)
{
	if(u!=1)
		siz[u]=1;
	bl(u,i)
	{
		ll v=e[i].to;
		dfs(v,tree);
		siz[u]+=siz[v];
		L[tree][u]=min(L[tree][u],L[tree][v]);
		R[tree][u]=max(R[tree][u],R[tree][v]);
	}
	f[tree][L[tree][u]][R[tree][u]]=max(f[tree][L[tree][u]][R[tree][u]],siz[u]);
}
int main()
{
	read(n);
	memset(L,INF,sizeof(L));
	rep(tree,0,1)
	{
		read(a);
		rep(i,1,a)
			head[i]=siz[i]=0;
		tot=0;
		rep(i,1,a-1)
		{
			read(pi);
			add_edge(pi,i+1,1,0);
		}
		rep(i,1,n)
		{
			read(pi);
			L[tree][pi]=i;
			R[tree][pi]=i;
		}
		dfs(1,tree);
	}
	rep(i,1,n)
		rep(tree,0,1)
			rep(j,0,i-1)
				dp[i]=max(dp[i],dp[j]+f[tree][j+1][i]);
	OP(dp[n]);
}