A: A Math Problem
题意:给出一个n,找出有多少个k满足kk <= 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 ", i); 37 } 38 }
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 ", ans); 87 } 88 } 89 return 0; 90 }
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是否有边。因为m≤min(2×105,n(n−1)/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 ",ans); 68 } 69 return 0; 70 }
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 ", ans.a[0][0]); 66 } 67 } 68 return 0; 69 }
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], " "[i == 40]); 35 // printf("%lld %lld %lld ", 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 ", bb, cc); 50 } 51 // cout << "bug "; 52 printf("%lld %lld %lld ", bb, cc, aa); 53 } 54 } 55 return 0; 56 }
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 ", 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 }
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 ", ans); 36 } 37 return 0; 38 }
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 ", 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 }
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 ",Query(x, l, r)); 116 } 117 } 118 return 0; 119 }
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 ", query(root[son[u]], x, 30, ord[u])); 98 } 99 } 100 return 0; 101 }
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 << " 33[32;1m" << #x << " -> "; err(x); } while (0) 4 void err() { cout << "