E
思路
设为打印出来的字符串的最大长度。设一个数组,如果有一个位于的左括号,就将的数值,与之相反地,右括号就。只要的最小值非负且,这个字符串就合法,最大嵌套深度(即需要的颜色数)就是。
用线段树维护数组,复杂度为,这里的不要取太大,大致取输入中的个数加一个常数即可。
代码
#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是可以得到正确答案的,因为对于没有维护进的区间(假设这个区间需要删两个节点才能全部覆盖,据此将其分为,并且这两个区间必然首尾相接),在求时必然会用到的答案,而已经考虑了第一个区间的影响,只需要在此基础上将的影响也考虑进去即可。所以这个大的区间被拆成两次维护进中,保证了正确性。
代码
#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]);
}