JZOJ #4708 / CODEFORCES #217 E.Alien DNA (逆向思维)

题目描述:

这题改编自CF217E,给出一串字符串,有$n$个操作,将区间$[l,r]$的字符中编号为奇数的写下,再把偶数的写在后面,然后把它们插入$r$和$r+1$之间,问最后字符串的前$k$位。

解题思路:

因为前面的操作不会对后面的操作有影响,所以我们考虑把操作倒过来做。相应的,一开始就只有$k$个位置有效,然后对于操作$[l,r]$,就把$[r+1,r+(r-l+1)]$标记为已操作,并记录每一位是哪一位转移过来的。然后对于前一个操作,就只能做那些未标记的位置。相当于对未标记的位置重标号,然后像刚刚一样继续做。当然,如果操作位置超出了当前未标记的位置,就不用做了。所以这样最多只要做$k$次。具体要怎么记录位置呢,可以用线段树,类似二分的找。在oj上被卡了线段树做法,所以改成非递归版卡过去了。

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 const int N = 3e6 + 10;
 7 int k, n, l[N], r[N], tr[N * 4], f[N];
 8 char s[N], ans[N];
 9 
10 void build(int o, int l, int r) {
11     tr[o] = r - l + 1;
12     if (l == r) return;
13     int m = l + r >> 1;
14     build(o << 1, l, m);
15     build(o << 1 | 1, m + 1, r);
16 }
17 
18 int find(int o, int l, int r, int k, int p) {
19     while (l != r) {
20         tr[o] -= p;
21         int m = l + r >> 1;
22         if (tr[o << 1] >= k) r = m, o <<= 1;
23         else k -= tr[o << 1], o = o << 1 | 1, l = m + 1; 
24     }
25     tr[o] -= p;
26     return l;
27 }
28 
29 void work() {
30     for (int i = n; i >= 1; i --) {
31         if (r[i] >= tr[1]) continue;
32         int t = l[i] & 1 ^ 1;
33         int now = l[i] + t;
34         if (now > r[i]) now = l[i] + (t ^ 1);
35         for (int j = 1; j <= r[i] - l[i] + 1 && r[i] < tr[1]; j ++) {
36             int p = find(1, 1, k, r[i] + 1, 1);
37             f[p] = find(1, 1, k, now, 0);
38             now += 2;
39             if (now > r[i]) now = l[i] + (t ^ 1);
40         }
41     }
42     for (int i = 1, j = 1; i <= k; i ++)
43         if (f[i]) ans[i] = ans[f[i]]; else ans[i] = s[j ++];
44     puts(ans + 1);
45 }
46 
47 int main() {
48     gets(s + 1);
49     scanf("%d %d", &k, &n);
50     build(1, 1, k);
51     for (int i = 1; i <= n; i ++) scanf("%d %d", &l[i], &r[i]);
52     work();
53     return 0;
54 }

 

posted @ 2016-08-22 20:29  Awner  阅读(327)  评论(0编辑  收藏  举报