Codeforces Round #581 Div2 A~D题解

本场链接:Codeforces Round #581 Div2

闲话

速度很拉胯,不够写D的.赛后发现比赛场上想写出D还是有点差距感觉.

A. BowWow and the Timetable

原题大意:给你一个整数,问比他严格小的形如(4^k)的数的个数.原数非常大,输入为原数的二进制表示的字符串.
数据范围:
(0 leq s leq 2^{100})

思路

一个比较好想到的点是(4)无非就是两个(2),答案还要算上(0),所以就是长度(n)的前提下,(n/2).如果除了第一位之外全是(0),这个是对的,因为本身表示的那个数取不到,他得是严格大于,如果有一个,就可以取到.因此还要加一个判断看是不是后面完全都是(0)的.到这里其实还差一点,因为之前的分析的的前提是这个数可以表示成一个(4)的幂的形式,但是如果(2)的指数是一个奇数,就不能表示了.因此只有偶数次幂的时候,有那个看是否全是(0)的判断,否则就没有.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int ll
signed main()
{
	ios::sync_with_stdio(0);cin.tie(0);
	string s;cin >> s;
	int n = s.size();
	int ok = 0;
	for(int i = 1;i < n;++i)
		if(s[i] != '0')
			ok = 1;
	printf("%lld
",n / 2 + (n % 2 == 1 ? ok : 0));
    return 0;
}

B. Mislove Has Lost an Array

原题大意:构造一个长度为(n)的序列,要求里面不同的元素的个数在([l,r])之间.并且每个元素要么是(1),要么是一个偶数,对于一个偶数的元素(a_i),必须保证(a_i/2)是存在于这个序列里的.问这个序列最小和最大的总和分别是多少.
数据范围:
(1 leq n leq 1000)
(1 leq l leq r leq min(n,20))

思路

由于这个题目的条件特别的强,导致了每个元素其实都是(2)的幂,所以可以从这个方向下手.又要保证说整个序列里不同的元素数量要在一个区间内,而这个区间的长度很小,所以可以枚举区间里的每一个数,直接遍历.假设当前要算的结果的是整个序列里有(i)个不同元素的,那么最小的时候,就是尽量塞(1)进去,但是又要满足不同数量,所以完全可以只搞出(i)个幂,就是(1,2,4,...2^i)这一段,剩下的全部填(1),这样就最小了,最大的也是同理,只要让后面的全部填(2^i)就可以了.最后全套一遍就可以了.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll qmul(ll a, ll b) 
{
  	ll L = a * (b >> 25LL)  * (1LL << 25) ;
  	ll R = a * (b & ((1LL << 25) - 1)) ;
  	return (L + R);
}
ll qpow(ll a,ll b)
{
	ll res = 1;
	while(b)
	{
		if(b & 1)	res = qmul(res,a);
		a = qmul(a,a);
		b >>= 1;
	}
	return res;
}
int main()
{
	int n,l,r;cin >> n >> l >> r;
	ll minv = qpow(2,l) - 1 + n - l;
	ll maxv = qpow(2,r) - 1 + (n - r) * qpow(2,r - 1);
	for(int i = l;i <= r;++i)
	{
		ll A = qpow(2,i) - 1 + n - i,B = qpow(2,i) - 1 + (n - i) * qpow(2,i - 1);
		minv = min({minv,A,B});
		maxv = max({maxv,A,B});
	}

	cout << minv << " " << maxv << endl;
    return 0;
}

C. Anna, Svyatoslav and Maps

原题大意:没法翻译,自行看题.

数据范围:
(2 leq n leq 100)

思路

定义关键点:一个关键点是线路上最短路必经的点,也就是序列里不会被删掉的点,显然(p_1,p_m)是关键点.顺序考虑每个点,如果当前这个(p_i)是一个关键点的话,意味着从上一个关键点走到(p_{i+1})是必须要经过(p_i)的,才会保留这个(p_i).设上一个关键点是(u),必须要经过的条件就是(u->p_i)的距离不等于(p_i->p_{i+1})的距离减掉(1).一旦删掉,这条路就会被一个最短路的点替代掉.所以做法已经非常显然了,就是跑一个(floyd)求出任意两点距离,再按照从前往后的顺序遍历找到所有的关键点就可以了,

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 105,M = 1e6+7;
char _g[N][N];
int g[N][N],f[N][N];
int p[M];
int main()
{
	int n;scanf("%d",&n);
	for(int i = 1;i <= n;++i)	scanf("%s",_g[i] + 1);
	for(int i = 1;i <= n;++i)
		for(int j = 1;j <= n;++j)
			f[i][j] = int(_g[i][j] - '0');
	for(int i = 1;i <= n;++i)
	{
		for(int j = 1;j <= n;++j)
			if(!f[i][j])
				f[i][j] = 0x3f3f3f3f;
		f[i][i] = 0;
	}
		
	//floyd
	for(int k = 1;k <= n;++k)
		for(int i = 1;i <= n;++i)
			for(int j = 1;j <= n;++j)
				f[i][j] = min(f[i][j],f[i][k] + f[k][j]);
				
	int m;scanf("%d",&m);
	for(int i = 1;i <= m;++i)	scanf("%d",&p[i]);
	vector<int> res;
	int last = p[1],pos = 1;res.push_back(last);
	for(int i = 2;i <= m - 1;++i)
	{
		if(f[last][p[i]] != f[last][p[i + 1]] - 1)
		{
			last = p[i];
			pos = i;
			res.push_back(p[i]);
		}
	}
	res.push_back(p[m]);
	printf("%d
",(int)res.size());
	for(auto& v : res)	printf("%d ",v);
    return 0;
}

D. Kirk and a Binary String

原题大意:给一个二进制字符串s,构造一个等长的字符串t,保证任意两个区间内的子串的最长不降子序列的长度相等.并且t里的0尽量多.
数据范围:
(1 leq |S| leq 2000) (easy)
(1 leq |S| leq 10^5)

思路

可以想到这样一个性质:对于一个0变成一个1是毫无意义的,不会让答案变得更好,甚至可能破坏性质.整个过程应该只有1变成0.那么整个序列的最长不降序列的长度记作(len),(0)的个数记为(cnt),那么整个序列里应该有(len-cnt)个1可以换成0.如果恰好相等则说明没有1可以换了,就是原串.因为最好情况就是整个不降序列就是一个纯0串.所以两者之差就是可以转换的数了.
考虑怎样的一个1可以被替换掉:如果以他为结尾的最长不降序列的长度恰好是应该修改的个数的话,就说明他是应该修改的最后一个数,因此可以从后往前依次看每个(1)是否满足这个性质,并修改掉当前还需要修改几个数.其次,还要做一个(dp)求出以1结尾的最长不降子序列的长度,分析到此整个题就比较容易了.

代码

using namespace std;
typedef long long ll;

const int N = 1e5+7;
char s[N];
int f[N],sum[N];
int main()
{
	scanf("%s",s + 1);
	int n =	strlen(s + 1);
	f[0] = 1;
	int cnt = 0;
	for(int i = 1;i <= n;++i)
	{
		f[i] = f[i - 1];sum[i] = sum[i - 1];
		if(s[i] == '1')	f[i] = max(f[i - 1] + 1,sum[i - 1] + 1);
		else	sum[i] = sum[i - 1] + 1,++cnt;
	}
	int len = f[n] - cnt,c = 0;
	for(int i = n;i >= 1;--i)
	{
		if(s[i] == '1' && f[i] == sum[i] + len - c)
		{
			++c;
			s[i] = '0';
		}
	}
	printf("%s",s + 1);
    return 0;
}
原文地址:https://www.cnblogs.com/HotPants/p/13475559.html