The 15-th Beihang University Collegiate Programming Contest - Final I+J题解

44 阅读4分钟

比赛链接

I

思路

首先一个显然的dp方程是:设fi,jf_{i,j}为过了ii天,选择了前jj个评分的最大价值。那么fi,j=max(fi1,x+g(x+1,j))f_{i,j}=max(f_{i-1,x}+g(x+1,j)),其中x[i1,j1]x\in[i-1,j-1]g(L,R)g(L,R)为区间[L,R][L,R]的价值。

gg进行预处理,考虑所给式子:

g(L,R)=(alaR)(alaR)g(L,R)=(a_l \land\dots\land a_R)\oplus(a_l\lor\dots\lor a_R)

cnticnt_i2i2^i[L,R][L,R]中出现的次数,当且仅当0<cnti<RL+10<cnt_i<R-L+12i2^i会为g(L,R)g(L,R)做出贡献(原因可以自己想一想)。因此先预处理每一个二进制位出现次数的前缀和,之后O(n2)O(n^2)枚举左右端点,再枚举每一个二进制位的出现次数是否合法来计算g(L,R)g(L,R)。复杂度为O(n2logai)O(n^2\log a_i)

预处理gg后dp复杂度为O(n2k)O(n^2k)

发现对于固定的RRg(L,R)g(L,R)的值随LL减小而增加,而且每次都是增加一个不曾有过的22的幂。所以g(L,R)g(L,R)最多有logai\log a_i种取值。在状态转移的时候,如果g(x,j)=g(x+1,j)g(x,j)=g(x+1,j),选择用fi1,x+g(x+1,j)f_{i-1,x}+g(x+1,j)转移一定不比用fi1,x1+g(x,j)f_{i-1,x-1}+g(x,j)转移差,因为在i1i-1天选了第x1x-1个之后,再选一个不会让答案变小。所以我们可以预处理出所有g(x,R)g(x,R)发生变化的位置进行转移,复杂度降为O(nklogai)O(nk\log a_i)

另外注意每天至少选一个评分,所以转移的位置要进行判断(WA on 5)。

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define per(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...);
}
/*#################################*/
const ll N=2005;
ll n,k;
ll a[N],f[N][N],sum[40][N],g[N][N];
vector<ll> ve[N];
void print()
{
	rep(i,1,n)
	{
		rep(j,1,i)
			op(g[j][i]);
		en;
	}
}
int main()
{
	read(n,k);
	rep(i,1,n)
		read(a[i]);
	rep(bit,1,34)
		rep(i,1,n)
			sum[bit][i]=sum[bit][i-1]+!!(a[i]&(1ll<<(bit-1)));
	rep(R,1,n)
	{
		rep(L,1,R)
		{
			rep(bit,1,34)
				if(sum[bit][R]-sum[bit][L-1] && sum[bit][R]-sum[bit][L-1]<R-L+1)
					g[L][R]+=1<<(bit-1);
			if(g[L][R]!=g[L-1][R])
				ve[R].emplace_back(L-1);
		}
		ve[R].emplace_back(R);
	}
	rep(i,1,k)
		rep(j,i,n)
			for(auto it:ve[j])
				if(it>=i)
					f[i][j]=max(f[i][j],f[i-1][it-1]+g[it][j]);
	OP(f[k][n]);
}

J

思路

把左括号看作11,右括号看作1-1,那么括号序列合法当且仅当任意前缀和sumi0sum_i\geq 0sumn=0sum_n=0

[L,R][L,R]翻转左右括号,对前缀和的影响可以分为三部分看:

  • [1,L1][1,L-1]没有影响
  • [L,R][L,R]不同的位置影响不同
  • [R+1,n][R+1,n]影响相同。

先解决[L,R][L,R],考虑其中任意位置ii,设[L,i][L,i]内左括号数目为cnt1cnt_1,右括号数目为cnt2cnt_2,有sumi=sumL1+cnt1cnt2sum_i=sum_{L-1}+cnt_1-cnt_2

在翻转左右括号后,cnt1cnt_1cnt2cnt_2互换,得到新的前缀和sumi=sumL1+cnt2cnt1sum_i'=sum_{L-1}+cnt2-cnt_1

作差:sumisumi=2cnt22cnt1=2sumL12sumisum_i'-sum_i=2cnt_2-2cnt_1=2sum_{L-1}-2sum_i

sumi=2sumL1sumisum_i'=2sum_{L-1}-sum_i

那么每次翻转相当于对[L,R][L,R]对前缀和整体取负数,然后加两倍的sumL1sum_{L-1},用线段树维护即可。

[R+1,n][R+1,n],直接减去sumRsum_R,翻转完[L,R][L,R]后在加上新的sumRsum_R即可。

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define per(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...);
}
/*#################################*/
#define lc p<<1
#define rc p<<1|1
const ll N=1e5+10;
string s;
ll n,m;
ll mini[4*N],maxi[4*N],rev[4*N],tag[4*N],sum[N];
void update(ll p)
{
	maxi[p]=max(maxi[lc],maxi[rc]);
	mini[p]=min(mini[lc],mini[rc]);
}
void build(ll p,ll l,ll r)
{
	if(l==r)
	{
		mini[p]=maxi[p]=sum[l]=sum[l-1]+(s[l]=='('?1:-1);
		return;
	}
	ll mid=(l+r)>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
	update(p);
}
void rtag(ll p)
{
	tag[p]=-tag[p];
	swap(mini[p],maxi[p]);
	mini[p]=-mini[p];
	maxi[p]=-maxi[p];
	rev[p]^=1;
}
void flag(ll p,ll val)
{
	mini[p]+=val;
	maxi[p]+=val;
	tag[p]+=val;
}
void push_down(ll p)
{
	if(rev[p])
	{
		rtag(lc);
		rtag(rc);
		rev[p]=0;
	}
	if(tag[p])
	{
		flag(lc,tag[p]);
		flag(rc,tag[p]);
		tag[p]=0;
	}
}
void neg(ll p,ll l,ll r,ll al,ll ar)
{
	if(al<=l && ar>=r)
	{
		rtag(p);
		return;
	}
	ll mid=(l+r)>>1;
	push_down(p);
	if(al<=mid)
		neg(lc,l,mid,al,ar);
	if(ar>mid)
		neg(rc,mid+1,r,al,ar);
	update(p);
}
void add(ll p,ll l,ll r,ll al,ll ar,ll val)
{
	if(al<=l && ar>=r)
	{
		flag(p,val);
		return;
	}
	ll mid=(l+r)>>1;
	push_down(p);
	if(al<=mid)
		add(lc,l,mid,al,ar,val);
	if(ar>mid)
		add(rc,mid+1,r,al,ar,val);
	update(p);
}
ll query(ll p,ll l,ll r,ll al,ll ar)
{
	if(al<=l && ar>=r)
		return mini[p];
	ll mid=(l+r)>>1,ret=1e17;
	push_down(p);
	if(al<=mid)
		ret=min(ret,query(lc,l,mid,al,ar));
	if(ar>mid)
		ret=min(ret,query(rc,mid+1,r,al,ar));
	return ret;
}
void print()
{
	rep(i,1,n)
		op(query(1,1,n,i,i));
	en;
}
int main()
{
	read(n,m);
	cin>>s;
	s=' '+s;
	build(1,1,n);
	ll l,r;
	rep(i,1,m)
	{
		read(l,r);
		ll _suml=0,sumr=query(1,1,n,r,r);
		if(l!=1)
			_suml=query(1,1,n,l-1,l-1);
		neg(1,1,n,l,r);
		add(1,1,n,l,r,2*_suml);
		if(r!=n)
		{
			add(1,1,n,r+1,n,-sumr);
			sumr=query(1,1,n,r,r);
			add(1,1,n,r+1,n,sumr);
		}
		if(query(1,1,n,1,n)>=0 && query(1,1,n,n,n)==0)
			puts("yes");
		else
			puts("no");
	}
}