2017ACM/ICPC Guangxi Invitational Solution

 

A: A Math Problem

题意:给出一个n,找出有多少个k满足k<= n

思路: kk的增长很快,当k == 16 的时候就已经超过1e18 了,对于每一次询问,暴力一下就可以

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 typedef long long ll;
 6 
 7 ll n;
 8 
 9 inline ll qpow(ll x, ll n)
10 {
11     ll ans = 1;
12     ll base = x;
13     while (n)
14     {
15         if (n & 1) ans = ans * base;
16         base = base * base;
17         n >>= 1;
18     }
19     return ans;
20 }
21 
22 int main()
23 {
24     while (scanf("%lld", &n) != EOF)
25     {
26         int i;
27         for (i = 1; i <= 15; i++)
28         {
29             if (qpow(i, i) > n)
30             {
31                 i--;
32                 break;
33             }
34         }
35         if (i == 16) i--;
36         printf("%d\n", i);
37     }        
38 }
View Code

 

B:Color it

题意:给出四种操作,

0 清空所有点

1 x y c  往(x, y) 处插入一个颜色为c的点

2 x y1 y2 查询(1, y1) 到(x, y2) 这个矩形里面有多少个颜色不同的点

3 退出程序

思路: 很显然 操作0 相当于多组样例 操作3是退出程序 故我们只需要考虑第1种操作和第2种操作

因为查询的范围当中x的左界是固定的,我们可以想到以y坐标轴来建线段树

因为只有51种颜色,我们考虑建51棵线段树,然后每次保存离左边界最近的横坐标,看看那个y1 - y2 那个区间内是否存在一个x' < x

存在的话就有这种颜色

然后开五十棵线段树,会爆内存,但是可以想到,每次更新最多增加log(n)的点,操作数量的上限也才150000, 故可以动态开点

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 #define INF 0x3f3f3f3f
 6 #define N 1000100
 7 
 8 struct node
 9 {
10     int l, r, v;
11     inline node() 
12     {
13         l = 0, r = 0, v = INF;
14     }
15     inline node(int l, int r, int v) : l(l), r(r), v(v) {}
16 }tree[N << 2];
17 
18 int root[55];
19 int cnt, tag;
20 
21 inline void Init()
22 {
23     for (int i = 0; i <= cnt; ++i)
24         tree[i] = node();
25     memset(root, 0, sizeof root);
26     cnt = 0;
27 } 
28 
29 inline void update(int &id, int l, int r, int x, int y)
30 {
31     if (id == 0)
32     {
33         id = ++cnt;
34         tree[id].v = x;
35     }
36     tree[id].v = min(tree[id].v, x);
37     if (l == r) return;
38     int mid = (l + r) >> 1;
39     if (y <= mid) update(tree[id].l, l, mid, x, y);
40     else update(tree[id].r, mid + 1, r, x, y);
41 }
42 
43 inline void query(int id, int l, int r, int ql, int qr, int x)
44 {
45     if (id == 0 || tag) return;
46     if (l >= ql && r <= qr)
47     {
48         if (tree[id].v <= x)
49             tag = 1;
50         return;
51     }
52     int mid = (l + r) >> 1;
53     if (ql <= mid) query(tree[id].l, l, mid, ql, min(mid, qr), x); 
54     if (qr > mid) query(tree[id].r, mid + 1, r, max(ql, mid + 1), qr, x);
55 }
56 
57 int main()
58 {
59     int op, x, y1, y2, c;
60     while (scanf("%d", &op) != EOF)
61     {
62         if (op == 3) return 0;
63         if (op == 0)
64         {
65             Init();
66         }
67         if (op == 1)
68         {
69             scanf("%d%d%d", &x, &y1, &c);
70             update(root[c], 1, 1000000, x, y1);
71         }
72         if (op == 2)
73         {
74             scanf("%d%d%d", &x, &y1, &y2);
75             if (y1 > y2) swap(y1, y2);
76             int ans = 0;
77             for (int i = 0; i <= 50; ++i)
78             {
79                 tag = 0;
80                 query(root[i], 1, 1000000, y1, y2, x);
81                 if (tag) 
82                 {
83                     ans++;
84                 }
85             }
86             printf("%d\n", ans);
87         }
88     }    
89     return 0;
90 }
View Code

 

 

C:Counting Stars

题意:给出一个无向图,没有重边,求有多少个"A-structure"结构。"A-structure"是指一个4个点的子图,这4个点依次连接,且有一条斜边。只要有任意一条边或一个点不同,则认为它们是不同的。

思路:选取一条边作为斜边,统计这个斜边上有多少个三元环,记为sum,那么以这个边为斜边的"A-structure"共有(sum-1)*sum/2个。但是直接枚举每条边找环的时间复杂度在完全图下可达m^2的级别,

这显然是不行的。枚举点x,然后枚举与x相邻的点y,这样相当于枚举了每条边,然后当x的度小于sqrt(m)时,我们可以用O(1)的时间判断y是否与x其他相邻的点组成三元环,而当x的度大于sqrt(m)时,我们

可以枚举与y相邻的点z,用set判断x与z是否有边。因为mmin(2×105,n(n1)/2),所以度大于sqrt(m)的点不会很多,这样时间复杂度就是msqrt(m)了。 

 1 #include <iostream>
 2 #include <iomanip>
 3 #include <fstream>
 4 #include <sstream>
 5 #include <cmath>
 6 #include <cstdio>
 7 #include <cstring>
 8 #include <cctype>
 9 #include <algorithm>
10 #include <functional>
11 #include <numeric>
12 #include <string>
13 #include <set>
14 #include <map>
15 #include <stack>
16 #include <vector>
17 #include <queue>
18 #include <deque>
19 #include <list>
20 using namespace std;
21 
22 const int N = 100005;
23 vector<int> g[N];
24 set<long long> p;
25 int fa[N], f[N], du[N];
26 int main() {
27     int n, m, i, j, k, x, y;
28     long long ans, sum;
29     while (scanf("%d %d", &n, &m) != EOF) 
30     {
31         ans = 0; p.clear();
32         for (i = 1; i <= n; ++i)
33         {
34             f[i] = fa[i] = du[i] = 0; g[i].clear();
35         }
36         for (i = 1; i <= m; ++i) 
37         {
38             scanf("%d %d", &x, &y);
39             g[x].push_back(y);
40             g[y].push_back(x);
41             p.insert((long long)x*n + y);
42             p.insert((long long)y*n + x);
43             du[x]++; du[y]++;
44         }
45         for (i = 1; i <= n; ++i)
46         {
47             f[i] = 1; 
48             for (j = 0; j < du[i]; ++j)    fa[g[i][j]] = i;
49             for (j = 0; j < du[i]; ++j)
50             {
51                 x = g[i][j];
52                 if (f[x] == 1)    continue;
53                 sum = 0;
54                 if (du[x] <= (int) sqrt(m))
55                 {
56                     for (k = 0; k < du[x]; ++k)
57                         if (fa[g[x][k]] == i)    sum++;
58                 }
59                 else
60                 {
61                     for (k = 0; k < du[i]; ++k)
62                         if (p.find((long long)x*n + g[i][k]) != p.end())    sum++;
63                 }
64                 ans += (sum - 1)*sum / 2;
65             }
66         }
67         printf("%lld\n",ans);
68     }
69     return 0;
70 }
View Code

 

 

D:Covering

题意:求用 1 * 2 和 2 * 1 的方块填充 4 * n 的矩形,求有多少方法

思路:n很大,显然是log(n)的做法,猜测应该是矩阵快速幂递推。用搜索或者状压DP暴力出前几项,发现前几项是这样的

1 1

2 5

3 11

4 36

5 95

6 281

7 781

满足递推式 F[n] = F[n - 1] + 5 * F[n - 2] + F[n - 3] - F[n - 4];

然后构造矩阵 矩阵快速幂一下

 

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 #define ll long long
 6 
 7 const int MOD = (int)1e9 + 7;
 8 
 9 struct node
10 {
11     ll a[4][4];
12     inline node() 
13     {
14         memset(a, 0, sizeof a);
15     }
16     inline node operator * (const node& r) const
17     {
18         node ans = node();
19         for (int i = 0; i < 4; ++i)
20             for (int j = 0; j < 4; ++j)
21                 for (int k = 0; k < 4; ++k)
22                     ans.a[i][j] = (ans.a[i][j] + (a[i][k] * r.a[k][j] + MOD) % MOD + MOD) % MOD;
23         return ans;
24     }
25 };
26 
27 ll arr[4][4] = 
28 {
29     1, 1, 0, 0, 
30     5, 0, 1, 0,
31     1, 0, 0, 1,
32    -1, 0, 0, 0,
33 };
34 
35 inline node qpow(ll n)
36 {
37     node ans = node();
38     ans.a[0][0] = 36, ans.a[0][1] = 11, ans.a[0][2] = 5, ans.a[0][3] = 1;
39     node base = node();
40     for (int i = 0; i < 4; ++i)
41         for (int j = 0; j < 4; ++j)
42             base.a[i][j] = arr[i][j];
43     while (n)
44     {
45         if (n & 1) ans = ans * base;
46         base = base * base;
47         n >>= 1;
48     }
49     return ans;
50 }
51 
52 ll n;
53 
54 int main()
55 {
56     while (scanf("%lld", &n) != EOF)
57     {
58         if (n == 1) puts("1");
59         else if (n == 2) puts("5");
60         else if (n == 3) puts("11");
61         else if (n == 4) puts("36");
62         else
63         {
64             node ans = qpow(n - 4);
65             printf("%lld\n", ans.a[0][0]);
66         }
67     }
68     return 0;
69 }
View Code

 

 

E:CS Course

题意:给出n个数,然后有q次查询,每次查询给出一个p,要求输出这n个数中,去掉第p个数的所有数分别进行按位与运算,按位异或运算,按位或运算的和

思路:首先考虑按位异或运算,根据异或运算的性质,我们只需要先预处理出所有数的异或和,然后每次输出时异或一下第p个数就行了 因为这样第p个数就异或了两次,相当于没有异或

再考虑按位与运算 我们也是先处理出所有数相与之后的那个和, 并且开一个数组记录一下每个二进制位上为1的数有多少, 再考虑以下几种情况

为了方便,我们假设第p个数为A,所有数相与之后的和为B,接下来我们再按二进制位考虑

1° 对于某一位上,B为0,并且A为0,并且所有数那一位上为1的个数为n - 1 那么我们可以知道,如果去掉A,那么剩下的数相与,那一位上肯定是1

2° 对于某一位上,B为1,很显然,去掉A之后,肯定也会为1

3° 对于某一位上,B为0,并且不满足第一种情况,那只能为0

 

按位或运算的思考方法和按位与运算思考方法差不多,此处故不再给出。

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 #define N 100010
 6 #define ll long long 
 7 
 8 int n, q;
 9 
10 ll arr[N];
11 
12 int a[60];
13 
14 int main()
15 {
16     while (scanf("%d%d", &n, &q) != EOF)
17     {
18         ll Xor = 0, And, Or = 0;
19         memset(a, 0, sizeof a);
20         for (int i = 1; i <= n; i++)
21         {
22             scanf("%lld", arr + i);
23             Xor ^= arr[i];
24             if (i == 1) And = arr[1];
25             else And &= arr[i];
26             Or |= arr[i];
27             ll tmp = arr[i];
28             for (int j = 0; j <= 40; j++)
29             {
30                 a[j + 1] += (tmp & (1ll << j)) ? 1 : 0;
31                 if ((1ll << j) > tmp) break;
32             }    
33         }
34 //        for (int i = 0; i <= 40; i++) printf("%d%c", a[i], " \n"[i == 40]);
35 //        printf("%lld %lld %lld\n", Xor, And, Or);
36         int p;
37         while (q--)
38         {
39             scanf("%d", &p);
40             ll aa = Xor ^ arr[p];
41             ll bb = 0; ll cc = 0;
42             for (ll i = 0; i <= 40; i++)
43             {
44                 if ((And & (1ll << i)) == 0 && a[i + 1] == n - 1 && (arr[p] & (1ll << i)) == 0) bb += (1ll << i);
45                 else if (And & (1ll << i)) bb += (1ll << i);
46                 if ((Or & (1ll << i)) && a[i + 1] == 1 && (arr[p] & (1ll << i))) continue;
47                 if ((Or & (1ll << i)) == 0) continue;
48                 cc += (1ll << i);
49 //                printf("%lld %lld\n", bb, cc);    
50             }
51 //            cout << "bug\n";
52             printf("%lld %lld %lld\n", bb, cc, aa);
53         }
54     }
55     return 0;
56 }
View Code

 

F:Destory Walls

题意:给出国王所在的坐标,以及一些点,再给出一些边,表示两个点之间有一堵墙,然后国王想拆掉一些墙,使得所有区域连通,使得拆掉墙的数量最少以及花费最少,没给出的边就说明两个点之间没有墙

思路:对偶图,是把面看成点,面与面之间连边,那么我们可以想到,如果给出的边关系构成的图没有环的话,那么只有一个面,也就是说所有区域都是连通的,那么我们就是想办法留下一棵树,使得边数尽量多,上面的权值和尽量大

那就是求一棵最大生成树

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define N 100010
 5 #define ll long long
 6 
 7 int n, m;
 8 int pre[N];
 9 ll tot;
10 
11 inline int find(int x)
12 {
13     if (pre[x] != x)
14         pre[x] = find(pre[x]);
15     return pre[x];
16 }
17 
18 struct Edge
19 {
20     int u, v; ll w;
21     inline void scan()
22     {
23         scanf("%d%d%lld", &u, &v, &w);
24         tot += w;
25     }
26     inline bool operator < (const Edge &r) const
27     {
28         return w > r.w;
29     }
30 }edge[N << 1];
31 
32 inline void Run()
33 {
34     while (scanf("%d%d", &n, &m) != EOF)
35     {
36         tot = 0;
37         for (int i = 1, x, y; i <= n; ++i) scanf("%d%d", &x, &y), pre[i] = i;
38         for (int i = 1; i <= m; ++i) edge[i].scan();
39         sort(edge + 1, edge + 1 + m);
40         int cnt = 0; ll sum = 0;
41         for (int i = 1; i <= m; ++i)
42         {
43             int u = edge[i].u, v = edge[i].v;
44             u = find(u), v = find(v);
45             if (u == v) continue;
46             ++cnt; sum += edge[i].w;
47             pre[u] = v;
48         }
49         printf("%d %lld\n", m - cnt, tot - sum);
50     }
51 
52 }
53 
54 int main()
55 {
56     #ifdef LOCAL
57         freopen("Test.in", "r", stdin);
58     #endif
59 
60     Run();
61 
62     return 0;
63 }
View Code

 

 

G:Duizi and Shunzi

题意: 有n个数,两个相同的数可以组成一个对子,三个连续的数可以组成一个顺子,数字不可以重复使用,求最后可以组成多少对对子和顺子

思路:贪心,我们优先的想法肯定是组对子,然后再是组顺子。因为组对子花的数少,组顺子花的数多。

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 #define N 1000010
 6 #define ll long long 
 7 
 8 int n;
 9 
10 ll arr[N];
11 
12 int main()
13 {
14     while (scanf("%d", &n) != EOF)
15     {
16         memset(arr, 0, sizeof arr);
17         for (int i = 1, num; i <= n; i++)
18         {
19             scanf("%d", &num);
20             arr[num]++;
21         }
22         ll ans = 0;
23         for (int i = 1; i <= n; ++i)
24         {
25             ans += arr[i] / 2;
26             ans += arr[i + 1] / 2;
27             arr[i] %= 2; arr[i + 1] %= 2;
28             if (arr[i] && arr[i + 1] && arr[i + 2]) 
29             {
30                 ans++;
31                 arr[i + 2]--;
32                 arr[i + 1]--;
33             }
34         }
35         printf("%lld\n", ans);
36     }
37     return 0;
38 }
View Code

 

 H - Law of Commutation

题意:给出n和a,求有多少个b 满足 $a^b \equiv b^a \pmod m$

思路:当a是奇数的时候 答案是1(我不知道为啥)

当a是偶数的时候 我们令 $a = 2 \cdot x$

那么 $a^b = 2^b \cdot x^b$

显然 当 b > n 的时候 $a^b \equiv 0 \pmod m$

那么当 b <= n 的时候 我们直接暴力

当b > n的时候 如果存在答案 那么有 $b^a \equiv 0 \pmod m$

那么 $b^a 是 m$ 的倍数 又 $m = 2 ^ n$
所以 b 一定是 $2 ^ {\frac{n}{a}}$ (向上取整,我不知道为啥) 的倍数

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define ll long long
 5 
 6 int n;
 7 ll a;
 8 
 9 inline ll qpow(ll x, ll n, ll MOD)
10 {
11     ll base = x;
12     ll ans = 1;
13     while (n)
14     {
15         if (n & 1) ans = ans * base % MOD;
16         base = base * base % MOD;
17         n >>= 1;
18     }
19     return ans;
20 }
21 
22 inline void Run()
23 {
24     while (scanf("%d%lld", &n, &a) != EOF)
25     {
26         if (a & 1)
27         {
28             puts("1");
29             continue;
30         }
31         ll ans = 0;
32         ll m = 1ll << n;
33         for (int i = 1; i <= n; ++i) if (qpow(a, i, m) == qpow(i, a, m))
34             ++ans;
35         ll tmp = (ll)ceil(n * 1.0 / a);
36         tmp = 1ll << tmp;
37         ans += (m / tmp - n / tmp);
38         printf("%lld\n", ans);
39     }
40 }
41 
42 int main()
43 {
44     #ifdef LOCAL
45         freopen("Test.in", "r", stdin);
46     #endif
47 
48     Run();
49 
50     return 0;
51 }
View Code

 

 

I:Matching in a Tree

留坑。

 

J:Query on A Tree

题意:给出一个树,有n个结点和q次询问,每次给出一个u和一个x,要在以结点u为根节点的子树中(包含u)中找出一个数与x异或后最大,输出异或后的数

思路:子树,自然想到用DFS序来处理这棵树,使得所有的儿子都在连续的一段区间里面,然后用可持久化Trie树去找异或最大

  1 #include <bits/stdc++.h>
  2 
  3 using namespace std;
  4 
  5 #define N 100010
  6 #define ll long long 
  7 
  8 struct Edge
  9 {
 10     int to, nx;
 11     inline Edge() {}
 12     inline Edge(int to, int nx) : to(to), nx(nx) {}
 13 }edge[N << 1];
 14 
 15 int head[N], tot, pos, cnt;
 16 int fa[N], son[N], ord[N], ford[N];
 17 int root[N];
 18 
 19 struct node
 20 {
 21     int son[2], Count;
 22     inline node() 
 23     {
 24         memset(son, 0, sizeof son);
 25         Count = 0;
 26     }    
 27 }tree[N * 64];
 28 
 29 inline void Init()
 30 {
 31     memset(head, -1, sizeof head);
 32     pos = 0; tot = 0, cnt = 0;
 33     tree[0] = node();
 34 }
 35 
 36 inline void addedge(int u, int v)
 37 {
 38     edge[++tot] = Edge(v, head[u]); head[u] = tot;
 39 }
 40 
 41 int n, q;
 42 ll w[N];
 43 
 44 inline void DFS(int u)
 45 {
 46     ord[u] = ++pos;
 47     ford[pos] = u;
 48     for (int it = head[u]; ~it; it = edge[it].nx)
 49     {
 50         int v = edge[it].to;
 51         if (v == fa[u]) continue;
 52         DFS(v);    
 53     }
 54     son[u] = pos;
 55 }
 56 
 57 inline void Insert(ll x, int id)
 58 {
 59     bitset <32> b; b = x;    
 60     root[id] = ++cnt;
 61     tree[cnt] = tree[root[id - 1]];
 62     int poss = cnt; 
 63     for (int i = 31; i >= 0; --i)
 64     {
 65         int index = b[i];
 66         tree[++cnt] = tree[tree[poss].son[index]];
 67         tree[cnt].Count++;
 68         tree[poss].son[index] = cnt;    
 69         poss = cnt;
 70     }
 71 }
 72 
 73 inline ll Query(ll x, int l, int r)
 74 {
 75     bitset <32> b; b = x;
 76     ll ans = 0; 
 77     l = root[l], r = root[r];
 78     for (int i = 31; i >= 0; --i)
 79     {
 80         int index = b[i] ^ 1;
 81         bool flag = true;
 82         if (tree[tree[r].son[index]].Count - tree[tree[l].son[index]].Count <= 0) 
 83         {
 84             index ^= 1; 
 85             flag = false;
 86         }
 87         if (flag) ans += (1 << i);  
 88         r = tree[r].son[index]; l = tree[l].son[index];
 89     }
 90     return ans;
 91 }
 92 
 93 int main()
 94 {
 95     while (scanf("%d%d", &n, &q) != EOF)
 96     {
 97         Init();
 98         for (int i = 1; i <= n; ++i) scanf("%lld", w + i);
 99         for (int i = 1, u; i < n; ++i)
100         {
101             scanf("%d", &u);
102             fa[i + 1] = u;
103             addedge(u, i + 1);
104         }
105         DFS(1);
106         for (int i = 1; i <= n; ++i)
107         {
108             Insert(w[ford[i]], i);
109         }
110         while(q--)
111         {
112             int u; ll x; 
113             scanf("%d%lld",&u,&x);
114             int l = ord[u] - 1, r = son[u];
115             printf("%lld\n",Query(x, l, r));
116         }
117     }
118     return 0;
119 }
View Code
  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 
  4 typedef long long ll;
  5 
  6 const int N = 2e5 + 10;
  7 
  8 vector<int>G[N];
  9 
 10 int n, q;
 11 
 12 int ord[N], son[N], v[N], get_ord[N];
 13 int index;
 14 
 15 int trie[N * 35][2], latest[N * 35];
 16 int root[N], tot;
 17 
 18 inline void init()
 19 {
 20     memset(trie, 0, sizeof trie);
 21     memset(latest, 0, sizeof latest);
 22     memset(root, 0, sizeof root);
 23     tot = 0;
 24     index = 0;
 25     for (int i = 0; i < N; ++i)
 26     {
 27         G[i].clear();
 28     }
 29 }
 30 
 31 inline void insert(int i, int k, int p, int q)
 32 {
 33     if (k < 0)
 34     {
 35         latest[q] = i;
 36         return;
 37     }
 38     int c = v[get_ord[i]] >> k & 1;
 39     if (p) trie[q][c ^ 1] = trie[p][c ^ 1];
 40     trie[q][c] = ++tot;
 41     insert(i, k - 1, trie[p][c], trie[q][c]);
 42     latest[q] = max(latest[trie[q][0]], latest[trie[q][1]]);
 43 }
 44 
 45 inline int query(int now, int val, int k, int limit)
 46 {
 47     if (k < 0)
 48     {
 49         return v[get_ord[latest[now]]] ^ val;
 50     }
 51     int c = val >> k & 1;
 52     if (latest[trie[now][c ^ 1]] >= limit)
 53         return query(trie[now][c ^ 1], val, k - 1, limit);
 54     else
 55         return query(trie[now][c], val, k - 1, limit);
 56 }
 57 
 58 inline void dfs(int u)
 59 {
 60     ord[u] = ++index;
 61     get_ord[index] = u;
 62     for (int i = 0; i < G[u].size(); i++)
 63     {
 64         int to = G[u][i];
 65         dfs(to);
 66     }
 67     son[u] = index;
 68 }
 69 
 70 int main()
 71 {
 72     while (~scanf("%d %d", &n, &q))
 73     {
 74         init();
 75         latest[0] = -1;
 76         root[0] = ++tot;
 77         insert(0, 30, 0, root[0]);
 78         for (int i = 1; i <= n; ++i)
 79         {
 80             scanf("%d", &v[i]);
 81         }
 82         for (int i = 2, u; i <= n; ++i)
 83         {
 84             scanf("%d", &u);
 85             G[u].push_back(i);
 86         }
 87         dfs(1);
 88         for (int i = 1; i <= n; ++i)
 89         {
 90             root[i] = ++tot;
 91             insert(i, 30, root[i - 1], root[i]);
 92         }
 93         while (q--)
 94         {
 95             int u, x;
 96             scanf("%d %d", &u, &x);
 97             printf("%d\n", query(root[son[u]], x, 30, ord[u]));
 98         }
 99     }
100     return 0;
101 }
View Code

 

K:Removing Mountains

题意:

给出一个字符串$S$,改变一个位置的字符(可以改成任意字符),使得循环节长度最小。

输出最小循环节长度以及可改动的字符位置

思路:

我们考虑枚举循环节长度$x$,如果有$s[1 \cdots n - x] = s[x + 1 \cdots n]$,那么循环节长度为$x$,并且每个位置都可以改变成原来的字符,其实相当于原串不变

否则我们考虑最长的$y$使得$s[1 \cdots y] = s[x + 1 \cdots x + y]$, 那么我们可以改变$s[y + 1]$或者$s[x + y + 1]$去判断是否能够使得$s[1 \cdots n - x] = s[x + 1 \cdots n]$成立,即至多改变两个位置

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define dbg(x...) do { cout << "\033[32;1m" << #x << " -> "; err(x); } while (0)
 4 void err() { cout << "\033[39;0m" << endl; }
 5 template <class T, class... Ts> void err(const T& arg, const Ts&... args) { cout << arg << ' '; err(args...); }
 6 const int N = 1e6 + 10, INF = 0x3f3f3f3f;
 7 int n; char s[N];
 8 
 9 typedef unsigned long long ull;
10 struct Hash {
11     static ull base[N]; 
12     static void init() {
13         base[0] = 1;
14         for (int i = 1; i < N; ++i)
15             base[i] = base[i - 1] * 131; 
16     }
17     ull a[N]; 
18     inline void gao(char *s) {
19         a[0] = 0;    
20         for (int i = 1; s[i]; ++i) {
21             a[i] = a[i - 1] * 131 + s[i]; 
22         }    
23     }
24     ull get(int l, int r, int x, int y) {
25         ull res = a[r] - a[l - 1] * base[r - l + 1];
26         if (x >= l && x <= r) {
27             res -= s[x] * base[r - x];
28             res += s[y] * base[r - x];
29         }
30         return res;
31     } 
32 }hs;
33 ull Hash::base[N] = {0};
34 
35 struct ExKMP {
36     int Next[N];
37     void get_Next(char *s) {
38         int lens = strlen(s + 1), p = 1, pos;
39         Next[1] = lens;
40         while (p + 1 <= lens && s[p] == s[p + 1]) ++p;
41         Next[pos = 2] = p - 1;
42         for (int i = 3; i <= lens; ++i) {
43             int len = Next[i - pos + 1];
44             if (len + i < p + 1) Next[i] = len;
45             else {
46                 int j = max(p - i + 1, 0);
47                 while (i + j <= lens && s[j + 1] == s[i + j]) ++j;
48                 p = i + (Next[pos = i] = j) - 1; 
49             }
50         }
51     }
52 }exkmp;
53 
54 int main() {
55     Hash::init(); 
56     while (scanf("%d", &n) != EOF) {
57         scanf("%s", s + 1);
58         exkmp.get_Next(s); hs.gao(s);
59         if (n == 1) {
60             puts("1 1");
61             continue;
62         }
63         int res = INF, num = INF; 
64         for (int i = 2; i <= n; ++i) {
65             int now = exkmp.Next[i];
66             if (now == n - i + 1) {
67                 res = i - 1;
68                 num = n;
69                 break;
70             } else {
71                 int x = 1 + now, y = i + now, len = n - i + 1; 
72                 num = 0;
73                 num += hs.get(1, len, x, y) == hs.get(i, n, x, y);
74                 num += hs.get(1, len, y, x) == hs.get(i, n, y, x);
75                 if (num) {
76                     res = i - 1;
77                     break;
78                 }
79             }
80         }
81         printf("%d %d\n", res, num);
82     }
83     return 0;
84 }
View Code

 

L:Yuno And lrotoridori no Sekai

按题意暴力模拟

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 
  4 #define N 1000010
  5 
  6 struct Edge
  7 {
  8     int to, nx;
  9     inline Edge() {}
 10     inline Edge(int to, int nx) : to(to), nx(nx) {}
 11 }edge[N << 1];
 12 
 13 int n, q;
 14 int head[N], pos;
 15 int w[N], path[N], deep[N], fa[N], tot;
 16 int rmq[N << 1], F[N << 1], P[N << 1], cnt;
 17 vector <int> vec;
 18 
 19 struct ST
 20 {
 21     int mm[N << 1];
 22     int dp[N << 1][20];
 23     inline void init(int n)
 24     {
 25         mm[0] = -1;
 26         for (int i = 1; i <= n; ++i)
 27         {
 28             mm[i] = ((i & (i - 1)) == 0) ? mm[i - 1] + 1 : mm[i - 1];
 29             dp[i][0] = i;
 30         }
 31         for (int j = 1; j <= mm[n]; ++j)
 32         {
 33             for (int i = 1; i + (1 << j) - 1 <= n; ++i)
 34             {
 35                 dp[i][j] = rmq[dp[i][j - 1]] < rmq[dp[i + (1 << (j - 1))][j - 1]] ? dp[i][j - 1] : dp[i + (1 << (j - 1))][j - 1]; 
 36             }
 37         }
 38     } 
 39     inline int query(int a, int b)
 40     {
 41         if (a > b) swap(a, b);
 42         int k = mm[b - a + 1];
 43         return rmq[dp[a][k]] <= rmq[dp[b - (1 << k) + 1][k]] ? dp[a][k] : dp[b - (1 << k) + 1][k];
 44     }
 45 }st;
 46 
 47 inline void Init()
 48 {
 49     memset(head, -1, sizeof head);
 50     pos = 0; deep[1] = 0; fa[1] = 1; 
 51 }
 52 
 53 inline void addedge(int u, int v)
 54 {
 55     edge[++pos] = Edge(v, head[u]); head[u] = pos;
 56     edge[++pos] = Edge(u, head[v]); head[v] = pos; 
 57 }
 58 
 59 inline void DFS(int u)
 60 {
 61     deep[u] = deep[fa[u]] + 1;
 62     F[++cnt] = u;
 63     rmq[cnt] = deep[u];
 64     P[u] = cnt;  
 65     for (int it = head[u]; ~it; it = edge[it].nx)
 66     {
 67         int v = edge[it].to;
 68         if (v == fa[u]) continue; 
 69         fa[v] = u; 
 70         DFS(v);
 71         F[++cnt] = u;
 72         rmq[cnt] = deep[u];
 73     }
 74 }
 75 
 76 inline void LCA_Init(int root, int node_num)
 77 {
 78     cnt = 0;
 79     DFS(root); 
 80     st.init(2 * node_num - 1); 
 81 }
 82 
 83 inline int query_lca(int u, int v)
 84 {
 85     return F[st.query(P[u], P[v])];
 86 }
 87 
 88 inline void query(int u)
 89 {
 90     vec.clear(); vec.push_back(w[u]);
 91     for (int it = head[u]; ~it; it = edge[it].nx)
 92     {
 93         int v = edge[it].to;
 94         vec.emplace_back(w[v]);
 95     }
 96     sort(vec.begin(), vec.end()); 
 97 }
 98 
 99 inline void Get(int u, int v)
100 {
101     int lca = query_lca(u, v);
102     tot = 0;
103     path[++tot] = u;
104     while (u != lca)
105     {
106         u = fa[u];
107         path[++tot] = u;
108     }
109     int mid = tot;
110     while (v != lca) 
111     {
112         path[++tot] = v;
113         v = fa[v];
114     }
115     reverse(path + 1 + mid, path + 1 + tot);
116 }
117 
118 inline void add(int v)
119 {
120     for (int i = 1; i <= tot; ++i)
121         w[path[i]] += v;
122 }
123 
124 inline void Reverse()
125 {
126     for (int i = 1, j = tot; i < j; ++i, --j)
127         swap(w[path[i]], w[path[j]]);
128 }
129 
130 inline void Run()
131 {
132     while (scanf("%d%d", &n, &q) != EOF)
133     {
134         Init();
135         for (int i = 1; i <= n; ++i) scanf("%d", w + i);
136         for (int i = 1, u, v; i < n; ++i) 
137         {
138             scanf("%d%d", &u, &v);
139             addedge(u, v);
140         }
141         LCA_Init(1, n); 
142         int op, x, y, z; 
143         while (q--)
144         {
145             scanf("%d%d%d", &op, &x, &y);
146             if (op == 3)
147             {
148                 query(x);
149                 while (y--) 
150                 {
151                     scanf("%d", &x);
152                     printf("%d\n", vec[x - 1]); 
153                 }
154             }
155             else
156             {
157                 Get(x, y);
158                 if (op == 1)
159                     Reverse();
160                 else
161                 {
162                     scanf("%d", &z);
163                     add(z);
164                 }
165             }
166         }
167     }
168 }
169 
170 int main()
171 {
172     #ifdef LOCAL
173         freopen("Test.in", "r", stdin);
174     #endif
175 
176     Run();
177 
178     return 0;
179 }
View Code

 

 

 


赛后总结:

  • 一定要记得return 0, ll
  • 如果矩阵快速幂递推的时候,项中有负的,一定要+MOD 再 % MOD,为保险起见,输出答案的时候再% MOD
  • 如果用i来表示答案,一定要注意跳出的情况,会不会有越界跳出
  • 位运算尽量要加括号,它的优先级最低,进行移位操作的时候,如果爆int了,一定要 1ll << x
  • 两个数相与,要么为0,要么为一个> 0 的数; 而不是 要么为0,要么为1 这个想法是错误了
  • 在改动了一处之后,一定要仔细思考一下,是否有另一处需要连带改动,不要做无畏的罚时增长
  • 对于没有写过的数据结构,但是觉得可做,一定要先想清楚,不可妄想,要用别的类似的数据结构的思想来写,或者一些优秀的数据结构也可以用到题目当中来优化常数
  • 尽量不要两个人用两种思路写同一题,尽量先交流好,毕竟只有一台电脑,交流是制胜的关键
  • 一定要加强数据结构,图论,数论的练习
  • 交题前一定要测试样例,尽量测试边界或极限数据,不能过样例就急着交

 

posted @ 2018-08-09 20:35  Dup4  阅读(256)  评论(0编辑  收藏  举报