Educational Codeforces Round 153 (Rated for Div. 2)

A. Not a Substring

题意:给定一个括号字符串,要求构造出一个长度为2*n的新串,使给定字符串作为连续的子串不出现在新串中。

思路:对于新串的构造无非只有((()))和()()()这两种,一一构造出来然后检验原串是否出现即可。

#include 
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
const int maxv = 4e6 + 5;
typedef pair pll;




void solve()
{
	string s;
	cin>>s;
	string a,b;
	for(int i=0;i> t;
	while (t--)
	{
		solve();
	}
	system("pause");
	return 0;
}

B. Fancy Coins

题意:有两种物品,一种价值为1,一种价值为k,每种物品会给定一些有限使用次数,若有限次数使用完后需要花费单位代价再次进行使用,现给定m,让两种物品的价值总和恰好为m且花费的单位代价最小(即有限次数的花费不算入单位代价中)

思路:贪心,对于给定的m,若其能整除k,那么这种是否我们只需要尽可能的使用k即可,对于价值为1的若我们有x*k个,同样相当于提供了x个k,但若m不能整除k,那么我们就让价值为1的物品去补足余数,剩下的尽可能去补k即可,若其连余数都无法补足,那么我们需要花费单位代价去用1补足余数即可。

#include 
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
const int maxv = 4e6 + 5;
typedef pair pll;




void solve()
{
	ll m,k,a,b;
	cin>>m>>k>>a>>b;
	int x=m%k;
	int y=m/k;
	if(a>=x){
		a-=x;
		a/=k;
		b+=a;
		cout<> t;
	while (t--)
	{
		solve();
	}
	system("pause");
	return 0;
}

C. Game on Permutation

题意:太长懒得翻译

思路:既然是博弈,那么肯定涉及到了必胜态和必败态一说,对于当前的i,若其能到达的点都为必败态,那么此时的状态即为必胜态,反之则为必败态,对于当前状态,若无法移动,则也为必败态,知道了上述条件,再结合题意,我们可以知道,我们当前点能够移动到的点为小于自己的点,那么我们根据这些小于自身的点的状态即可判断当前状态。

思考应该如何去维护上述过程:我们可以知道,树状数组的经典运用:可以维护逆序对,即树状数组可以计算出遍历到当前点时,前方有多少个点比自己小,已知这个作用,那么我们还剩一个信息没有维护,即我们怎么去统计前方比自己点小的状态。我们假定必胜态为1,必败态为0,那么我们同样可以使用树状数组去统计,因为对于当前点i,树状数组维护的是1-i的前缀和,那么我们可以知道,所有已经出现了的比自己小的点肯定在当前i的前面,那么若1-i的前缀和为 0,就代表着,前方所有点的状态都为必败态,那么当前点的状态就为必胜态,反之,若前缀和不为0,则代表着前方至少有一个点的状态为必胜态,那么当前点的状态就是必败态,我们将当前点的状态放入树状数组即可。

#include 
using namespace std;
const int N = 3e5 + 5;
typedef long long ll;
const int maxv = 4e6 + 5;
typedef pair pll;


int a[N];

struct MIT
{
ll tr[N];
int lowbit(int x) {
    return x & (-x);
}

void add(int u, int v) {
    for (int i = u; i < N; i += lowbit(i)) {
        tr[i] += v;
    }
}

ll query(int x) {
    ll res = 0;

    for (int i = x; i > 0; i -= lowbit(i)) {
        res += tr[i];
    }

    return res;
}
};

MIT t1,t2;
//t1维护的是到目前为止,前方有多少个比自己小的点
//t2则是维护的比自己小的点的信息

void solve()
{
	int n;
	cin>>n;
	int cnt=0;
	memset(t1.tr,0,sizeof t1.tr);
	memset(t2.tr,0,sizeof t2.tr);
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++){
		int x1=t1.query(a[i]);
		int x2=t2.query(a[i]);
		if(x1==0){//x1==0代表当前点前方没有点,根据题意,这种状态也为必败态
			t1.add(a[i],1);//那么我们直接把它加入到t1即可
		}
		else if(x2==0){
			t2.add(a[i],1);//如果当前点前方全为必败态,那么当前点就是必胜态
			t1.add(a[i],1);
			cnt++;
		}

	}
	cout<> t;
	while (t--)
	{
		solve();
	}
	system("pause");
	return 0;
}

还有一种做法可以在O(n)的时间复杂度内求出,即我们维护两个变量,一个为到目前为止的最小值x,一个为必胜态的最小值y,对于位置i,考虑当前位置在什么情况下合法,即ai必须大于x,因为得保证前方有点可以进行移动,且ai的数量即可

你可能感兴趣的:(cf补题,算法,数据结构)