AtCoder Regular Contest 066 题解

D - Xor Sum

为什么这个D这么难啊
首先显然有 v ≤ u v \le u vu,那么只要保证 v ≤ n v \le n vn
f [ n ] f[n] f[n] v ≤ n v \le n vn的合法点对和, v = a ⊕ b v=a \oplus b v=ab
然后数位dp,考虑 a , b a,b a,b的最后一位,有同位0,一个1,和都是1三种情况,分别转移过来。
1和2,2和3两种情况显然是独立的,而1,3因为末位一样,似乎会重复。
假设会算重复,那么就要存在 a ⊕ b = c ⊕ d a \oplus b=c \oplus d ab=cd a + b + 1 = c + d a+b+1=c+d a+b+1=c+d ,显然不存在。
记忆化搜索即可。
code:

#include
#include
#include
#include
#include
#define LL long long
using namespace std;
const LL mod=1e9+7;
map<LL,LL> f;
LL n;
LL dfs(LL x)
{
	if(x<0) return 0;
	if(x==0) return 1;
	if(f.count(x)) return f[x];
	f[x]=(dfs(x>>1)+dfs((x-1)>>1)+dfs((x-2)>>1))%mod;
	return f[x];
}
int main()
{
	scanf("%lld",&n);
	printf("%lld",dfs(n));
}

E - Addition and Subtraction Hard:

注意到只有-后面的括号才有效,且至多套两层括号,直接dp即可。

#include
#include
#include
#include
#define LL long long
using namespace std;
const LL inf=1LL<<60;
LL n,a[100010],f[100010][3];
char op[100010][2];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++) scanf("%d %s",&a[i],op[i]);
    scanf("%d",&a[n]);
    f[1][0]=a[1];f[1][1]=f[1][2]=-inf;
    for(int i=2;i<=n;i++)
        if(op[i-1][0]=='+')
        {
            f[i][0]=max(f[i-1][0],f[i-1][1])+a[i];
            f[i][1]=f[i-1][1]-a[i];
            f[i][2]=f[i-1][2]+a[i];
        }
        else
        {
            f[i][0]=-inf;
            f[i][1]=max(f[i-1][0],f[i-1][1])-a[i];
            f[i][2]=max(f[i-1][1],f[i-1][2])+a[i];
        }
    printf("%lld",max(f[n][0],max(f[n][1],f[n][2])));
    return 0;
}

F - Contest with Drinks Hard:

先考虑一次询问怎么做,设 f [ i ] f[i] f[i]表示前 i i i位的答案,有方程式:
f [ i ] = m a x ( f [ i − 1 ] , f [ j ] + ( i − j ) ∗ ( i − j + 1 ) 2 − ( s u m [ i ] − s u m [ j ] ) ) f[i]=max(f[i-1],f[j]+\frac{(i-j)*(i-j+1)}{2}-(sum[i]-sum[j])) f[i]=max(f[i1],f[j]+2(ij)(ij+1)(sum[i]sum[j]))
容易得到对策单调性是向左的,用单调栈维护斜率即可。
然后考虑修改。
f 1 f1 f1为前缀答案, f 2 f2 f2为后缀答案,假如修改位不选,那么就是 f 1 [ x − 1 ] + f 2 [ x + 1 ] f1[x-1]+f2[x+1] f1[x1]+f2[x+1]
然后设 g [ x ] g[x] g[x]为强制选第 x x x为的最大值,此时答案为 g [ x ] − y + a [ x ] g[x]-y+a[x] g[x]y+a[x]
怎么求 g g g
分治,每层更新所有 m i d ≤ x ≤ r mid\le x\le r midxr g g g,所以求出每个点为右端点,且左端点在左区间的答案,再取后缀最大值。最后反过来在做一次。
wa两个点,可能被卡精,不管了
code:

#include
#include
#include
#include
#include
#define LL long long
using namespace std;
LL n,a[300010],f1[300010],f2[300010],sum[300010],g[300010],t[300010];
LL sta[300010],top=0;
LL Y(LL x,LL *f) {return 2*f[x]+x*x-x+2*sum[x];}
long double slope(LL j1,LL j2,LL *f) {return (long double)(Y(j1,f)-Y(j2,f))/(j1-j2);}
void pre(LL *f)
{
	f[0]=0;sta[1]=0;top=1;
	for(LL i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
	for(LL i=1;i<=n;i++)
	{
		while(top>1&&slope(sta[top],sta[top-1],f)<=2*i) top--;
		LL j=sta[top];
		LL len=i-j;f[i]=max(f[i-1],f[j]+len*(len+1)/2-(sum[i]-sum[j]));
		while(top>1&&slope(sta[top],i,f)>=slope(sta[top-1],sta[top],f)) top--;
		sta[++top]=i;
	}
}
void solve(LL l,LL r,LL *f1,LL *f2)
{
	if(l==r) return;
	LL mid=(l+r)/2;
	sta[1]=l-1;top=1;
	for(LL i=l;i<mid;i++)
	{
		while(top>1&&slope(sta[top],sta[top-1],f1)<=2*i) top--;
		sta[++top]=i;
	}
	for(LL i=mid;i<=r;i++)
	{
		while(top>1&&slope(sta[top],sta[top-1],f1)<=2*i) top--;
		LL j=sta[top];
		LL len=i-j;t[i]=f1[j]+len*(len+1)/2-(sum[i]-sum[j])+f2[i+1];
	}
	for(LL i=r-1;i>=mid;i--) t[i]=max(t[i],t[i+1]);
	for(LL i=r;i>=mid;i--) g[i]=max(g[i],t[i]);
	solve(l,mid,f1,f2);solve(mid+1,r,f1,f2);
}
int main()
{
	scanf("%lld",&n);
	for(LL i=1;i<=n;i++) scanf("%lld",&a[i]);
	pre(f1);reverse(a+1,a+n+1);pre(f2);reverse(a+1,a+n+1);reverse(f2+1,f2+n+1);
	for(LL i=1;i<=n;i++) sum[i]=sum[i-1]+a[i],g[i]=-a[i];
	solve(1,n,f1,f2);reverse(a+1,a+n+1);reverse(f1+1,f1+n+1);reverse(f2+1,f2+n+1);reverse(g+1,g+n+1);
	for(LL i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
	solve(1,n,f2,f1);reverse(a+1,a+n+1);reverse(f1+1,f1+n+1);reverse(f2+1,f2+n+1);reverse(g+1,g+n+1);
	LL Q;scanf("%lld",&Q);
	while(Q--)
	{
		LL x,y;scanf("%lld %lld",&x,&y);
		printf("%lld\n",max(f1[x-1]+f2[x+1],g[x]-y+a[x]));
	}
}

你可能感兴趣的:(atcoder)