【题解】Codeforces 1054 : Mail.Ru Cup 2018 Round 1 ABCD

版权声明:欢迎评论交流,转载请注明原作者。 https://blog.csdn.net/m0_37809890/article/details/83187913

上上次看错卡简单题,上次想偏卡中期题,这次卡了脑洞题,三场掉蓝,RP=max。

总结

  1. 简单的计算题最好先在纸上列出算式,并且算一下样例,不要直接上手。
  2. 构造题,先写一个正向的程序来找思路,最后也可以拿它来检验。
    如果能正常跑完正向程序的话,除了值域与正向程序之外不用再写其它判断构造失败的条件。
  3. 看到区间求和有关的东西,就自动去想前缀和了!

A. Elevator or Stairs? 计算

当前在x层,要去y层(x≠y),可以选择走楼梯或者电梯。当前电梯在z层,电梯移动一层花费t2时间,电梯开门或者关门需要t3时间,走一层楼梯需要t1时间。当使用电梯的时间小于使用楼梯的时间时,输出YES,vice versa。

走楼梯时间 a = t 1 a b s ( x y ) a = t1 * abs(x-y)
坐电梯时间 b = t 2 a b s ( z y ) + t 3 + t 3 + a b s ( x y ) + t 3 b = t2 * abs(z-y)+t3+t3+abs(x-y)+t3 ,需要开门两次,关门一次
列好算式,比较即可。

B. Appending Mex 模拟

定义mex()函数,输入一个可重集合,返回集合中最小的未出现的非负整数。
维护一个集合,初始时刻为空,每次操作选取它的一个子集求mex并将结果添加到集合最后。
现在给出一个集合,问是否可以通过上边的操作形成的,如果不行,输出最早的假的步数。

模拟,维护当前集合中的最大值即可。

C. Candies Distribution 构造

n(1000)个数的序列,告诉每个位置的左边有多少有多少个比它的值大的,右边有多少个比它的值大的,求是否有这样的序列,要求值域在1到n之间。

把左和右加起来,就知道了有多少个数大于这个数了,这个位置的值就是n-lef[i]-rig[i]。最后检验一下答案是否正确。

int lef[M],rig[M],ans[M];
int main(void)
{
	int n = read();
	for(int i=1;i<=n;++i)
		lef[i] = read();
	for(int i=1;i<=n;++i)
		rig[i] = read();
	for(int i=1;i<=n;++i)
		ans[i] = n-lef[i]-rig[i];

	int fail = 0;
	for(int i=1;i<=n;++i)
	{
		int xl=0,xr=0;
		for(int j=1;j<=n;++j) if(ans[j]>ans[i])
		{
			if(j<i) ++xl;
			if(j>i)	++xr;
		}
		if(xl!=lef[i] || xr!=rig[i])
			fail = 1;
	}
	if(fail)
		printf("NO\n");
	else
	{
		printf("YES\n");
		for(int i=1;i<=n;++i)
			printf("%d ",ans[i] );
		printf("\n");
	}
    return 0;
}

D. Changing Array 前缀和

给定n(2e5),k(30),和n个k位数组成的序列。在每个数都可以任意取反的情况下,求最多有多少子串异或和不为0.

如果序列不变,怎么求有多少子串异或和不为0?
a n s = Σ i = 1 n Σ j = i n ( k = i j a k &gt; 0 ) = Σ i = 1 n Σ j = i n ( s j s i 1 &gt; 0 ) ans = \Sigma _{i=1}^n\Sigma _{j=i}^n(\oplus _{k=i}^ja_k&gt;0)=\Sigma _{i=1}^n\Sigma _{j=i}^n(s_j\oplus s_{i-1}&gt;0) ,其中 s i = j = 1 i a j s_i=\oplus _{j=1}^ia_j ,即前缀异或和。
所以答案就是n*(n-1)/2 - 所有值出现的次数分别组合 - 为0的前缀和个数。(不想写数学公式了)

如果每个数可以取反,那么每个前缀和也可以取反。把取反后相同的前缀和尽量平均地分配即可,注意0与~0时,要把0的个数减去。

n*n会爆int

/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 500016, MOD = 1000000007;

int save[M];
int main(void)
{
	#ifdef _LITTLEFALL_
	freopen("in.txt","r",stdin);
    #endif

	int n = read(), k = read();
	for(int i=1;i<=n;++i)
		save[i] = read()^save[i-1];
	int mx = (1<<k)-1;
	map<int,int> mp;
	for(int i=1;i<=n;++i)
		++mp[min(save[i],mx-save[i])];
	ll ans = (ll)n*(n+1)/2;
	for(auto pii:mp)
	{
		ll a = pii.second/2, b = (pii.second+1)/2;
		ans -= a*(a-1)/2 + b*(b-1)/2;
		if(pii.first==0)
			ans-=a;
	}
	printf("%I64d\n",ans );

    return 0;
}


inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

猜你喜欢

转载自blog.csdn.net/m0_37809890/article/details/83187913