zoukankan      html  css  js  c++  java
  • 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
    ", (int)ans.size());
    	for(auto it = ans.begin(); it != ans.end(); ++it) {
    		printf("%d %d
    ", 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
    ", 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
    ", ret);
    	}
    
    	return 0;
    }
    

      

  • 相关阅读:
    winform+c#之窗体之间的传值 Virus
    ASP.NET 2.0 利用 checkbox获得选中行的行号, 在footer中显示 Virus
    .NET中的winform的listview控件 Virus
    我的书橱
    Expert .NET 2.0 IL Assembler·译者序一 写在一稿完成之即
    Verbal Description of Custom Attribute Value
    AddressOfCallBacks in TLS
    下一阶段Schedule
    2008 Oct MVP OpenDay 第二天 博客园聚会
    2008 Oct MVP OpenDay 第二天 颁奖·讲座·晚会
  • 原文地址:https://www.cnblogs.com/rolight/p/4260789.html
Copyright © 2011-2022 走看看