CodeForces R285 Div2

落下好多,趁着假期慢慢补吧。。

C. Misha and Forest

因为是一个森林,所以可以先找到所有的叶子节点,然后进行递推即可。开一个队列搞就好了。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <stack>

using namespace std;

typedef long long LL;
const int maxn = 1 << 17;
int n, xsum[maxn], cnt[maxn];
bool vis[maxn];

set< pair<int, int> > ans;

int main() {
	scanf("%d", &n);
	queue<int> q;
	for(int i = 0; i < n; i++) {
		scanf("%d%d", &cnt[i], &xsum[i]);
		if(cnt[i] == 1) {
			q.push(i);
			vis[i] = true;
		}
	}
	while(!q.empty()) {
		int now = q.front(); q.pop();
		if(cnt[now] == 0) continue;
		int u = now, v = xsum[now];
		if(u > v) swap(u, v);
		ans.insert(make_pair(u, v));
		xsum[xsum[now]] ^= now; 
		cnt[xsum[now]]--;
		if(cnt[xsum[now]] == 1 && !vis[xsum[now]]) {
			q.push(xsum[now]); 
			vis[xsum[now]] = true;
		}
	}
	printf("%d\n", (int)ans.size());
	for(auto it = ans.begin(); it != ans.end(); ++it) {
		printf("%d %d\n", it->first, it->second);
	}
	return 0;
}

Misha and Permutations Summation

利用康拓展开得到展开之后进行相加,取模之后化简,观察一下很容易就发现做法了。。

中间可能要用一下线段树或者是树状数组来降低一下复杂度。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <stack>

using namespace std;

typedef long long LL;
const int maxn = 2e5 + 10;
int p1[maxn], p1t[maxn], p2[maxn], p2t[maxn], n;
int apt[maxn], ans[maxn];

int C[maxn];

int lowbit(int x) {
	return x & (-x);
}

int init_bit() {
	memset(C, 0, sizeof(C));
}

void addv(int pos, int x, int mx) {
	while(pos <= mx) {
		C[pos] += x;
		pos += lowbit(pos);
	}
}

int ask(int pos) {
	int ret = 0;
	while(pos > 0) {
		ret += C[pos];
		pos -= lowbit(pos);
	}
	return ret;
}

void calc(int arr[], int tar[], int len) {
	init_bit();
	for(int i = 0; i < len; i++) {
		addv(i + 1, 1, len);
	}
	for(int i = len - 1; i >= 0; i--) {
		tar[i] = ask(arr[i]);
		addv(arr[i] + 1, -1, len);
	}
}

int findk(int val, int mx) {
	int l = 1, r = mx, ret = 1;
	while(l <= r) {
		int mid = (l + r) >> 1;
		if(ask(mid) >= val) {
			ret = mid;
			r = mid - 1;
		}
		else {
			l = mid + 1;
		}
	}
	return ret;
}

void recalc(int arr[], int tar[], int len) {
	init_bit();
	for(int i = 0; i < len; i++) {
		addv(i + 1, 1, len);
	}
	for(int i = len - 1; i >= 0; i--) {
		tar[i] = findk(arr[i] + 1, len) - 1;
		addv(tar[i] + 1, -1, len);
	}
}

int main() {
	scanf("%d", &n);
	for(int i = 0; i < n; i++) {
		scanf("%d", &p1[i]);
	}
	reverse(p1, p1 + n);
	calc(p1, p1t, n);
	for(int i = 0; i < n; i++) {
		scanf("%d", &p2[i]);
	}
	reverse(p2, p2 + n);
	calc(p2, p2t, n);
	for(int i = 0; i < n; i++) {
		apt[i] += p1t[i] + p2t[i];
		apt[i + 1] += apt[i] / (i + 1);
		apt[i] %= (i + 1);
	}
	recalc(apt, ans, n);
	for(int i = n - 1; i >= 0; i--) {
		printf("%d ", ans[i]);
	}
	puts("");
	return 0;
}

 E Misha and Palindrome Degree

这题没想出来,看了题解才会的。。

首先根据题意,很容易想到的是找到所有的最小区间,然后通过容斥原理求解。 并且在这之前可以先进行统计,如果有个数为奇数的字符种类数超过1的话可以直接判不成立。

首先找到最大的i使得 任意k < i 有 a[i] = a[n - i + 1], 不难发现最小区间必然有一个边界是i 或者 n - i + 1

然后分别固定左边界和右边界,然后来找最小区间的位置。可以发现这里是具有单调性的,所以可以用二分来降低复杂度,二分最小区间的长度。

至于这个区间是否合法可以用O(n)来判断。

对于每一个位置i,有以下几种情况,并且会以以下的次序出现(以左区间端点固定为例),并且设当前区间是[l, r]

首先是  i < l, n - i + 1 > r,此时必然有a[i] = a[n - i + 1]成立,可以不做判断

然后是 r >= i >= l, n - i + 1 > r,或者是i < l,n - i + 1 <= r, 此时区间中必须要有大于等于1个对应字符,否则不成立,这里要在之前做一些统计并且判断

然后是 l <= i <= n - i + 1 <= r,设这里的i为i0,必然有当i < i0外面那部分都是回文的,在进行二分之前判断过字符串必定可以构成回文的,所以此时剩下的字符必然也可以构成回文,所以不必判断。

最后是 r < i <= n - i + 1,这里要判断a[i]是否和a[n - i + 1]相等。

之后容斥原理随意搞即可。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <stack>

//using namespace std;

typedef long long LL;
const int maxn = 1e5 + 10;
int a[maxn], n, cl, cr, count[maxn], odd;

bool check(int lpos, int rpos) {
	memset(count, 0, sizeof(count));
	for(int i = lpos; i <= rpos; i++) {
		count[a[i]]++;
	}
	for(int i = 1; i < n - i + 1; i++) {
		bool inL = (i >= lpos && i <= rpos);
		bool inR = (n - i + 1 >= lpos && n - i + 1 <= rpos);
		if(inL && inR) continue;
		else if(inL) {
			if(--count[a[n - i + 1]] < 0) return false;
		}
		else if(inR) {
			if(--count[a[i]] < 0) return false;
		}
		else {
			if(a[i] != a[n - i + 1]) return false;
		}
	}
	return true;
}

LL calc(int pos) {
	int l = pos, r = n, tar;
	while(l <= r) {
		int mid = (l + r) >> 1;
		if(check(pos, mid)) {
			tar = mid;
			r = mid - 1;
		}
		else {
			l = mid + 1;
		}
	}
	return (LL)pos * (LL)(n - tar + 1);
}

int main() {
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
		count[a[i]]++;
	}

	for(int i = 1; i <= n; i++) {
		if(count[i] & 1) {
			odd++;
		}
	}

	cl = 1;
	cr = n;

	while(cl <= cr && a[cl] == a[cr]) {
		cl++; cr--;
	}

	if(cl > cr) {
		LL ret = (LL)n * (LL)(n - 1) / 2 + n;
		printf("%I64d\n", ret);
	}
	else if(odd > 1) {
		puts("0");
	}
	else {
		LL ret = -(LL)cl * (LL)(n - cr + 1);
		ret += calc(cl);
		std::reverse(a + 1, a + 1 + n);
		ret += calc(cl);
		printf("%I64d\n", ret);
	}

	return 0;
}

  

 

posted @ 2015-01-29 20:12  acm_roll  阅读(125)  评论(0编辑  收藏  举报