题目大意 给定一个大小为n的数组,从中选出一个子集使得这个子集中的数的和能被n整除。
假设开始我没有做出来,那么我就random_shuffle一下,然后计算前缀和,有一个能被n整除,就输出答案。于是这道题就过了。(数据水得起飞)
考虑计算前缀和,如果存在两个前缀和在模n的意义同余,那么就有可以将两个前缀和相减得到的一段区间的和,它的和就是n的倍数。
考虑这么做的正确性,模n的意义下有n个数,但是前缀和总共有(n + 1)个数。
Code
1 #include <iostream> 2 #include <fstream> 3 #include <sstream> 4 #include <cstdio> 5 #include <cstdlib> 6 #include <cstring> 7 #include <cmath> 8 #include <ctime> 9 #include <cctype> 10 #include <algorithm> 11 #include <vector> 12 #include <bitset> 13 #include <queue> 14 #include <stack> 15 #include <set> 16 #include <map> 17 #ifdef WIN32 18 #define Auto "%I64d" 19 #else 20 #define Auto "%lld" 21 #endif 22 using namespace std; 23 typedef bool boolean; 24 typedef pair<int, int> pii; 25 #define smin(_a, _b) _a = min(_a, _b) 26 #define smax(_a, _b) _a = max(_a, _b) 27 28 template<typename T> 29 inline void readInteger(T& u) { 30 char x; 31 while(!isdigit(x = getchar())); 32 for(u = x - '0'; isdigit(x = getchar()); u = u * 10 + x - '0'); 33 } 34 35 int n; 36 int* a; 37 int s = 0; 38 int L, R; 39 40 inline void init() { 41 readInteger(n); 42 a = new int[(n + 1)]; 43 memset(a, -1, sizeof(int) * (n + 1)); 44 a[0] = 0; 45 for(int i = 1, x; i <= n; i++) { 46 readInteger(x); 47 s = (s + x) % n; 48 if(a[s] != -1) { 49 L = a[s] + 1, R = i; 50 return; 51 } 52 a[s] = i; 53 } 54 } 55 56 inline void solve() { 57 printf("%d\n", R - L + 1); 58 for(int i = L; i <= R; i++) 59 printf("%d ", i); 60 } 61 62 int main() { 63 freopen("set.in", "r", stdin); 64 freopen("set.out", "w", stdout); 65 init(); 66 solve(); 67 return 0; 68 }
题目大意 有n本书,每次只能选择和上次种类不同的书阅读,问最少有多少本书看不了。
仔细分析题目可以得到一个信息:如果种类最多的那本书的数量大于n的一半,那么答案就是它的两倍减n减1。否则答案为0。
由于我只关心出现次数超过一半的众数的出现次数,因此有了以下三种解法(2骗分 + 1正解)
Solution 1 (抽样法I)
随机抽取一些位置,求出它们的众数,然后再带进原序列中求出现次数。
Code
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <fstream> 3 #include <sstream> 4 #include <cstdio> 5 #include <cstdlib> 6 #include <cstring> 7 #include <cmath> 8 #include <ctime> 9 #include <cctype> 10 #include <algorithm> 11 #include <vector> 12 #include <bitset> 13 #include <queue> 14 #include <stack> 15 #include <set> 16 #include <map> 17 #ifdef WIN32 18 #define Auto "%I64d" 19 #else 20 #define Auto "%lld" 21 #endif 22 using namespace std; 23 typedef bool boolean; 24 typedef pair<int, int> pii; 25 #define ll long long 26 #define smin(_a, _b) _a = min(_a, _b) 27 #define smax(_a, _b) _a = max(_a, _b) 28 29 template<typename T> 30 inline void readInteger(T& u) { 31 char x; 32 while(!isdigit(x = getchar())); 33 for(u = x - '0'; isdigit(x = getchar()); u = u * 10 + x - '0'); 34 } 35 36 int m, k; 37 int n = 0; 38 int *counter; 39 int *X, *Y, *Z, S; 40 41 int myrand() { 42 return rand() << 15 | rand(); 43 } 44 45 inline void init() { 46 readInteger(m); 47 readInteger(k); 48 counter = new int[(m + 1)]; 49 X = new int[(m + 1)]; 50 Y = new int[(m + 1)]; 51 Z = new int[(m + 1)]; 52 S = (1 << k) - 1; 53 for(int i = 1; i <= m; i++) { 54 readInteger(counter[i]); 55 n += counter[i]; 56 } 57 for(int i = 1; i <= m; i++) 58 readInteger(X[i]); 59 for(int i = 1; i <= m; i++) 60 readInteger(Y[i]); 61 for(int i = 1; i <= m; i++) 62 readInteger(Z[i]); 63 } 64 65 const int randTime = 101; 66 int pos[randTime]; 67 68 inline void solve() { 69 for(int i = 1; i < randTime; i++) 70 pos[i] = myrand() % n; 71 sort(pos + 1, pos + randTime); 72 int len = unique(pos + 1, pos + randTime) - pos; 73 74 int last, p = 1, cnt = 0, id = -1; 75 for(int i = 1; i <= m && p < len; i++) { 76 last = X[i]; 77 if(cnt == pos[p]) 78 pos[p++] = last; 79 cnt++; 80 for(int j = 1; j < counter[i]; j++, cnt++) { 81 last = (last * 1LL * Y[i] + Z[i]) & S; 82 if(cnt == pos[p]) 83 pos[p++] = last; 84 } 85 } 86 87 pos[0] = -1; 88 sort(pos + 1, pos + len); 89 int cmp = 0, maxcnt = 0; 90 for(int i = 1; i < len; i++) { 91 if(pos[i] != pos[i - 1]) 92 cmp = 0; 93 if(++cmp > maxcnt) 94 maxcnt = cmp, id = pos[i]; 95 } 96 97 cnt = 0; 98 for(int i = 1; i <= m; i++) { 99 cnt += (last = X[i]) == id; 100 for(int j = 1; j < counter[i]; j++) 101 cnt += (last = (last * 1LL * Y[i] + Z[i]) & S) == id; 102 } 103 104 if(n - cnt >= cnt - 1) 105 printf("0"); 106 else 107 printf("%d", 2 * cnt - n - 1); 108 } 109 110 int main() { 111 freopen("read.in", "r", stdin); 112 freopen("read.out", "w", stdout); 113 srand(233); 114 init(); 115 solve(); 116 return 0; 117 }
Solution 2 (抽样法II)
抽取每一段前10个,求出它们的众数,然后再带回原序列中求出现次数。
显然数据很水所以过了,然后求众数时,我没有排序,还是过了(这。。。)
Code
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <fstream> 3 #include <sstream> 4 #include <cstdio> 5 #include <cstdlib> 6 #include <cstring> 7 #include <cmath> 8 #include <ctime> 9 #include <cctype> 10 #include <algorithm> 11 #include <vector> 12 #include <bitset> 13 #include <queue> 14 #include <stack> 15 #include <set> 16 #include <map> 17 #ifdef WIN32 18 #define Auto "%I64d" 19 #else 20 #define Auto "%lld" 21 #endif 22 using namespace std; 23 typedef bool boolean; 24 typedef pair<int, int> pii; 25 #define ll long long 26 #define smin(_a, _b) _a = min(_a, _b) 27 #define smax(_a, _b) _a = max(_a, _b) 28 29 template<typename T> 30 inline void readInteger(T& u) { 31 char x; 32 while(!isdigit(x = getchar())); 33 for(u = x - '0'; isdigit(x = getchar()); u = u * 10 + x - '0'); 34 } 35 36 int m, k; 37 int n = 0; 38 int counter[1005]; 39 int X[1005], Y[1005], Z[1005], S; 40 41 int myrand() { 42 return rand() << 15 | rand(); 43 } 44 45 inline void init() { 46 readInteger(m); 47 readInteger(k); 48 S = (1 << k) - 1; 49 for(int i = 1; i <= m; i++) { 50 readInteger(counter[i]); 51 n += counter[i]; 52 } 53 for(int i = 1; i <= m; i++) 54 readInteger(X[i]); 55 for(int i = 1; i <= m; i++) 56 readInteger(Y[i]); 57 for(int i = 1; i <= m; i++) 58 readInteger(Z[i]); 59 } 60 61 int pos[10005]; 62 63 inline void solve() { 64 int len = 0; 65 for(int i = 1; i <= m; i++) { 66 pos[++len] = X[i]; 67 for(int j = 1; j <= counter[i] && j < 6; j++) 68 pos[++len] = (pos[len - 1] * Y[i] + Z[i]) & S; 69 } 70 71 pos[0] = -1; 72 int cmp = 0, id, maxcnt = 0, cnt, last; 73 for(int i = 1; i < len; i++) { 74 if(pos[i] != pos[i - 1]) 75 cmp = 0; 76 if(++cmp > maxcnt) 77 maxcnt = cmp, id = pos[i]; 78 } 79 80 cnt = 0; 81 for(int i = 1; i <= m; i++) { 82 cnt += (last = X[i]) == id; 83 for(int j = 1; j < counter[i]; j++) 84 cnt += (last = (last * Y[i] + Z[i]) & S) == id; 85 } 86 87 if(n - cnt >= cnt - 1) 88 printf("0"); 89 else 90 printf("%d", 2 * cnt - n - 1); 91 } 92 93 int main() { 94 freopen("read.in", "r", stdin); 95 freopen("read.out", "w", stdout); 96 // srand(233); 97 init(); 98 solve(); 99 return 0; 100 }
Solution 3 (求和法)
因为它出现次数大于一半,所以考虑用一个 cnt 和一个 id
枚举序列中每个数,如果 cnt == 0 ,那么就将 id 赋值为当前枚举的这个数,并将cnt置为1。
否则,如果当前的这个数和 id 相等,就将 cnt 的值加1,否则减1。
这个算法完成后,我们会得到一个是出现次数超过n的一半的众数或者一个诡异的数,最后再把得到的id带回去求次数。
这么做的正确性显然(虽然解释不了但是觉得显然正确啊)。
Code
1 #include <iostream> 2 #include <fstream> 3 #include <sstream> 4 #include <cstdio> 5 #include <cstdlib> 6 #include <cstring> 7 #include <cmath> 8 #include <ctime> 9 #include <cctype> 10 #include <algorithm> 11 #include <vector> 12 #include <bitset> 13 #include <queue> 14 #include <stack> 15 #include <set> 16 #include <map> 17 #ifdef WIN32 18 #define Auto "%I64d" 19 #else 20 #define Auto "%lld" 21 #endif 22 using namespace std; 23 typedef bool boolean; 24 typedef pair<int, int> pii; 25 #define ll long long 26 #define smin(_a, _b) _a = min(_a, _b) 27 #define smax(_a, _b) _a = max(_a, _b) 28 29 template<typename T> 30 inline void readInteger(T& u) { 31 char x; 32 while(!isdigit(x = getchar())); 33 for(u = x - '0'; isdigit(x = getchar()); u = u * 10 + x - '0'); 34 } 35 36 int m, k; 37 int n = 0; 38 int *counter; 39 int *X, *Y, *Z, S; 40 41 inline void init() { 42 readInteger(m); 43 readInteger(k); 44 counter = new int[(m + 1)]; 45 X = new int[(m + 1)]; 46 Y = new int[(m + 1)]; 47 Z = new int[(m + 1)]; 48 S = (1 << k) - 1; 49 for(int i = 1; i <= m; i++) { 50 readInteger(counter[i]); 51 n += counter[i]; 52 } 53 for(int i = 1; i <= m; i++) 54 readInteger(X[i]); 55 for(int i = 1; i <= m; i++) 56 readInteger(Y[i]); 57 for(int i = 1; i <= m; i++) 58 readInteger(Z[i]); 59 } 60 61 int cnt = 0, id; 62 63 inline void add(int x) { 64 if(cnt == 0) 65 id = x, cnt = 1; 66 else if(id == x) 67 cnt++; 68 else 69 cnt--; 70 } 71 72 inline void solve() { 73 int last; 74 for(int i = 1; i <= m; i++) { 75 add(last = X[i]); 76 for(int j = 1; j < counter[i]; j++) 77 add(last = (last * 1LL * Y[i] + Z[i]) & S); 78 } 79 80 cnt = 0; 81 for(int i = 1; i <= m; i++) { 82 cnt += (last = X[i]) == id; 83 for(int j = 1; j < counter[i]; j++) 84 cnt += (last = (last * 1LL * Y[i] + Z[i]) & S) == id; 85 } 86 87 if(n - cnt >= cnt - 1) 88 printf("0"); 89 else 90 printf("%d", 2 * cnt - n - 1); 91 } 92 93 int main() { 94 freopen("read.in", "r", stdin); 95 freopen("read.out", "w", stdout); 96 init(); 97 solve(); 98 return 0; 99 }
题目大意 (题目太简洁,无法概括大意)
因为涉及到了可恶的位运算,为了更好地处理它们,所以想到Trie树。
如果Trie树的一个非叶节点在两天中表示的名次在a ~ b之间,设它的两棵子树的大小分别为s1和s2。
那么左子树表示的区间就是a ~ (a + s1 - 1)和(a + s2) ~ b,右子树同理。
因为最终到了叶节点,表示的区间都变成a ~ a的形式,并且我们关心的只是平方和。
所以考虑如何维护所有开始端点的平方和。
写写式子发现:
由于然后发现再维护一下所有左端点的和就可以搞定了。
写代码的时候可以用黑科技优化,不建Trie树就可以直接搞答案。先将A数组排序,然后对于每一层都进行二分查找这一位0和1的分界位置。
Code
1 #include <iostream> 2 #include <fstream> 3 #include <sstream> 4 #include <cstdio> 5 #include <cstdlib> 6 #include <cstring> 7 #include <cmath> 8 #include <ctime> 9 #include <cctype> 10 #include <algorithm> 11 #include <vector> 12 #include <bitset> 13 #include <queue> 14 #include <stack> 15 #include <set> 16 #include <map> 17 #ifdef WIN32 18 #define Auto "%I64d" 19 #else 20 #define Auto "%lld" 21 #endif 22 using namespace std; 23 typedef bool boolean; 24 typedef pair<int, int> pii; 25 #define ll long long 26 #define smin(_a, _b) _a = min(_a, _b) 27 #define smax(_a, _b) _a = max(_a, _b) 28 29 template<typename T> 30 inline void readInteger(T& u) { 31 static char x; 32 while(!isdigit(x = getchar())); 33 for(u = x - '0'; isdigit(x = getchar()); u = u * 10 + x - '0'); 34 } 35 36 const int M = 1e9 + 7; 37 38 int n, m; 39 int ans = 0; 40 ll S; 41 int* A; 42 43 inline void init() { 44 readInteger(n); 45 readInteger(m); 46 A = new int[(n + 1)]; 47 S = (1ll << (m - 1)); 48 for(int i = 1; i <= n; i++) 49 readInteger(A[i]); 50 } 51 52 void dfs(int dep, int L, int R, ll sum, ll sum2) { 53 if(L == R) { 54 ans ^= (sum2 % M); 55 return; 56 } 57 int l = L, r = R; 58 while(l <= r) { 59 int mid = (l + r) >> 1; 60 if(A[mid] & (1 << dep)) r = mid - 1; 61 else l = mid + 1; 62 } 63 int s1 = r - L + 1, s2 = R - r; 64 if(r >= L) dfs(dep - 1, L, r, sum + S * s2, sum2 + (ll)sum * s2 + S * s2 * s2); 65 if(R > r) dfs(dep - 1, r + 1, R, sum + S * s1, sum2 + (ll)sum * s1 + S * s1 * s1); 66 } 67 68 inline void solve() { 69 sort(A + 1, A + n + 1); 70 dfs(m - 1, 1, n, 0, 0); 71 printf("%d", ans); 72 } 73 74 int main() { 75 freopen("race.in", "r", stdin); 76 freopen("race.out", "w", stdout); 77 init(); 78 solve(); 79 return 0; 80 }