Codeforces Round #572 Div2 A~E题解

本场链接:Codeforces Round #572 Div2

闲话

这场打得很牛逼,最后148名.不过这场主要是结论题,就不做太多解释了.D2比较神仙,就空着以后再填了.

A. Keanu Reeves

题目大意:给定一个二进制字符串s,定义一个字符串是牛逼的,当且仅当这个串里的0和1的个数不同.现求将这个s分成若干个连续的子串,且每个子串都是牛逼的,最少要分成几段,并输出具体方案.
数据范围:
(1 leq |s| leq 100)

思路

由于数据范围太小了,完全可以乱搞搞.因为对于每个子串来说,都必须是连续的,所以一个比较明显的贪心想法就是从开始往后看最长能找到的牛逼的子串的右端点在哪,可以发现这样是正确的,因为两个较短的方案组合在一起一定对应一个最长加一截的方案.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
	ios::sync_with_stdio(0);cin.tie(0);
	int n;cin >> n;
	string s;cin >> s;
	vector<string> p;
	for(int i = 0;i < n;++i)
	{
		if(s[i] == '#')	continue;
		int m = i,z = 0,o = 0;
		
		for(int j = i;j < n;++j)
		{
			if(s[j] == '0')	++o;
			else ++z;
			if(z != o)	m = j;
		}
		string res;
		for(int j = i;j <= m;++j)	
		{
			res += s[j];
			s[j] = '#';
		}
		p.push_back(res);
	}
	cout << p.size() << endl;
	for(auto& v : p)	cout << v << " ";
    return 0;
}

B. Number Circle

原题大意:给定一个(n)元素的序列a,把他按某种顺序组成一个环,要求整个环上的每个元素都满足相邻的两个元素之和严格大于他.要求构造一个具体方案,或说明无解.

思路

一个最直接的想法就是排序,直接输出.但是显然这个题不能这么干,比如这个数据:5 2 3 3 5 7.如果直接排序的话,会认为7这个元素是不合法的,因为左右两个元素分别是2和5.但是显然又有一种构造方案:7 3 2 3 5是合法的.问题在于:最小和次大的一起可能是跟最大一样的,换言之,这个题的构造方案应该是一种平均分配的和,而不是直接排序从小到大的和.
于是思路就比较明确了,既然要平均分配,那就先排序再按奇偶性区分元素,最后判断这样是否有解,输出即可

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+7;
int a[N];
int main()
{
	int n;scanf("%d",&n);
	for(int i = 1;i <= n;++i)	scanf("%d",&a[i]);
	sort(a + 1,a + n + 1);
	vector<int> res;
	for(int i = 1;i <= n;i += 2)	res.push_back(a[i]);
	reverse(res.begin(),res.end());
	// for(auto& v : res)	cout << v << " ";cout << endl;
	for(int i = 2;i <= n;i += 2)	res.push_back(a[i]);
	for(int i = 1;i <= n;++i)	a[i] = res[i - 1];
	// for(int i = 1;i <= n;++i)	cout << a[i] << " ";cout << endl;
	int ok = 1;
	if(a[n] + a[2] <= a[1])	ok = 0;
	if(a[n - 1] + a[1] <= a[n])	ok = 0;
	for(int i = 2;i <= n - 1;++i)
		if(a[i - 1] + a[i + 1] <= a[i])
			ok = 0;
	if(!ok)	puts("NO");
	else
	{
		puts("YES");
		for(int i = 1;i <= n;++i)	printf("%d ",a[i]);
	}
    return 0;
}

C. Candies!

原题大意:给定一个序列a,保证他的长度是2的幂.现规定一种操作,对于一段区间长度是2的幂的子序列来说,按每两个连续的相邻元素为一组,划分完之后计算每组两个元素的和,如果某个和大于10,则增加一个糖果,并以和(\%10)为新的值,重复过程直到整个序列只有一个元素为止.给出(q)个询问,每个询问包含一个区间,问这段区间最终可以获得几个糖果.
数据范围:
(1 leq n leq 10^5)
(1 leq s_i leq 9)
(1 leq q leq 10^5)

思路

由于这个题的数据范围过于牛逼,可以推算到每个询问的处理复杂度要么(O(1))要么(O(logn)).那么既然是整个区间的和,不难想到应该是求一下整个区间的和,那么整个区间产生的贡献就是整个区间里包含几个(10).整除即可.
为什么他是正确的呢.可以感性的理解一下:如果总和不过10,那么这部分必然传到下一位,如果超过,则整个去掉10再走到下一步,不管情况怎样,不会有一个情况是两边组合超过10,但各自取模之后不过10,即矛盾的情况的.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+7;
int a[N];
int main()
{
	int n;scanf("%d",&n);
    for(int i = 1;i <= n;++i)	scanf("%d",&a[i]);
    for(int i = 1;i <= n;++i)	a[i] += a[i - 1];
    int q;scanf("%d",&q);
    while(q--)
    {
    	int l,r;scanf("%d%d",&l,&r);
    	printf("%d
",(a[r] - a[l - 1]) / 10);
    }
    return 0;
}

D1. Add on a Tree

原题大意:给定一个(n)个点的无根树,每次可以找到两个叶子节点,将两个叶子节点之间的简单路径上的所有边一起加减一个实数.问对于这个树来说是否有可能让任意一种情况都可以在有限次操作下出现,即不存在某种情况是在有限次操作下根本不能出现的.
数据范围:
(2 leq n leq 10^5)

思路

乍一看这个题目非常牛逼.不过在模拟了样例之后可以发现,对于一个度数为2的点,如果让一边是1一边是0,就会导致问题无解,因为这两个边必然一同被经过,必然不可能出现一边和另外一边不同.但是这个结论不能推广,比如存在度数为(4)的点是可以的,一个点连四个点,这样情况是有解的.继续可以猜到这应该就是整个题目的结论了.直接查即可.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+7;
int deg[N];
int main()
{
	int n;scanf("%d",&n);
	for(int i = 1;i < n;++i)
	{
		int u,v;scanf("%d%d",&u,&v);
		++deg[u];++deg[v];
	}
	int ok = 1;
	for(int i = 1;i <= n;++i)
	{
		if(deg[i] == 2)
		{
			ok = 0;
			break;
		}
	}
	if(!ok)	puts("NO");
	else puts("YES");
    return 0;
}

D2 过于牛逼,过段时间再写吧.

E. Count Pairs

原题大意:给定一个长度为(n)的序列a,求满足(1 leq i < j leq n)((a_i+a_j)*(a_i^2+a_j^2)equiv k (mod p))的个数,其中(k)是正整数,(p)是质数.
数据范围:
(2 leq n leq 3*10^5)
(2 leq p leq 10^9)
(0 leq k leq n - 1)
(0 leq a_i leq p - 1)

思路

这个形式一边是平方和一边是和,有点不太好看.有几个方向都可以尝试一下,比如三次方和展开,尝试了一些之后发现都不太行.正确的思路是两边乘一个((a_i - a_j))使左边是一个平方差一个平方和,进而继续缩小形式,得到这个式子:(a_i^4-a_j^4equiv k(a_i-a_j) (mod p)),继续把右边拉到左边,发现这个表达是就等价于(p | a_i^4-a_j^4-k(a_i-a_j)),继续可以拿到一个集中的形式:(p | a_i(a_i^3-k) - a_j(a_j^3-k))于是就相当于定义了一个(c_i = a_i(a_i^3-k)),并且要求对于每一个(c_i)来说有多少个在他之前的(c_j)满足与它模(p)同余.使用(map)统计即可.
注意有减法形式,防范负数取余.打一个快速幂和快速乘即可轻松套过本题.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5+7;
ll a[N];
ll qmul(ll a, ll b, ll P) 
{
  	ll L = a * (b >> 25LL) % P * (1LL << 25) % P;
  	ll R = a * (b & ((1LL << 25) - 1)) % P;
  	return (L + R) % P;
}
ll qpow(ll a,ll b,ll p)
{
	ll res = 1 % p;
	while(b)
	{
		if(b & 1)	res = qmul(res,a,p);
		a = qmul(a,a,p);
		b >>= 1;
	}
	return res;
}
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);
	ll n,p,k;cin >> n >> p >> k;
	map<ll,int> tb;
	int res = 0;
	for(int i = 1;i <= n;++i)	cin >> a[i];
	for(int i = 1;i <= n;++i)
	{
		ll r = ((qpow(a[i],3,p) - k) % p + p) % p;
		r = qmul(r,a[i],p);
		if(tb.count(r))	res += tb[r];
		++tb[r];
	}
	cout << res;
    return 0;
}
原文地址:https://www.cnblogs.com/HotPants/p/13475547.html