【9.23校内测试】【抽屉原理】【乱搞??(找众数】【Trie】
看到题目一开始想到的是一道求子集和的异或和,可以用$bitset$实现求子集和。然而这道题如果要强算子集和肯定是带不动的,况且还要算方案,所以尝试去找题目中的性质。
看到整除,很容易想到如果是一段区间,区间的头和尾的前缀和模后余数是一样的,那么这段区间(左开右闭)一定是满足整除的一段区间。而这道题目中,我们发现模数$n$很特殊,是这个序列的长度。继续深入思考。
这个序列一共有$n$个前缀和,而模$n$取余数一共有$n$个,其中如果余数是0那么便是符合条件的答案了,所以如果0,剩下$n-1$个余数对应到$n$个前缀和的位置,一定至少有两个位置的余数是一样的!这就是小学奥数的抽屉原理叻!因此得证,满足题目要求的方案一定至少有一种是连续的一段区间。(同时也说明题目中不可能有不存在满足条件的子集的情况
#include<bits/stdc++.h> #define LL long long #define RG register using namespace std; int n; LL a[1000005]; void read(LL &x) { x = 0; char ch = getchar(); while(ch > '9' || ch < '0') ch = getchar(); while(ch>= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } } LL ans = 0; LL pre[1000005], las[1000005]; int main() { freopen("set.in", "r", stdin); freopen("set.out", "w", stdout); scanf("%d", &n); int tot = 0; int pos = 0; for(int i = 1; i <= n; i ++) { read(a[i]); pre[i] = (pre[i-1] + a[i]) % n; if(las[pre[i]] || pre[i] == 0) { pos = i; break; } las[pre[i]] = i; } printf("%d\n", pos - las[pre[pos]]); for(int i = las[pre[pos]] + 1; i <= pos; i ++) printf("%d ", i); return 0; } /* 4 1 5 6 7 */
这道题题目balabala说了一大堆,实际上就是要求数量最多的那个种类书的数量,如果$cnt>(n+1)/2$,那么就不能把剩下的书都包完,剩下的就是题目要求的不能看的书,而其他情况都可以使所有书都能被看。
而题目最大的限制就是$n$的范围,不然直接开桶排序扫一遍记录最大值即可。所以我们要想的就是在这样的基础上优化空间。
所以显然是不能把$a$数组存下来的,所以考虑在生成每一个$a$的时候处理出一些内容。我们可以记录一个$cnt$和$id$,$id$表示当前数量最大的数(这个当前不好理解,如果前面最大的数被其它数数量抵消了,那么就把前面那部分完全抛开当作没有,从新的起点开始这个$id$记录的就可能是抵消完后一个数量并不是最大的数)。如果当前$a$等于这个$id$,那么$cnt++$,否则$cnt--$,如果$cnt == 0$,意思是前面都被抵消完了,那么就重新开始,更新$id$。像这样扫一遍。
得出的这个$id$,如果它的数量$>=(n+1)/2$,那么它记录的就是全局众数,像最开始说的那样统计答案就行了。而如果它的数量没有达到,说明全局中没有数量$>=(n+1)/2$(如果全局中有,这个$id$就一定会统计成那个数),因此所有书都可以被看。所以我们用$id$为判断条件再去扫一遍统计它的数量就行了。
#include<bits/stdc++.h> #define LL long long using namespace std; LL cot[1005], X[1005], Y[1005], Z[1005]; int m, k, n; int main() { freopen("read.in", "r", stdin); freopen("read.out", "w", stdout); scanf("%d%d", &m, &k); for(int i = 1; i <= m; i ++) scanf("%lld", &cot[i]), n += cot[i]; for(int i = 1; i <= m; i ++) scanf("%lld", &X[i]); for(int i = 1; i <= m; i ++) scanf("%lld", &Y[i]); for(int i = 1; i <= m; i ++) scanf("%lld", &Z[i]); int S = (1 << k) - 1; LL id, num = 0; LL las; for(int i = 1; i <= m; i ++) { las = X[i]; if(num == 0) { id = las; num = 1; } else if(las == id) num ++; else num --; for(int j = 1; j < cot[i]; j ++) { las = (las * Y[i] + Z[i]) & S; if(num == 0) { id = las; num = 1; } else if(las == id) num ++; else num --; } } num = 0; for(int i = 1; i <= m; i ++) { las = X[i]; if(las == id) num ++; for(int j = 1; j < cot[i]; j ++) { las = (las * Y[i] + Z[i]) & S; if(las == id) num ++; } } if(num > (n+1) / 2) { int tmp = n - num + 1; printf("%d", num - tmp); } else printf("0"); return 0; } /* 4 2 1 1 1 1 1 1 1 2 0 0 0 0 0 0 0 0 */
目前还没有完全理解并且改代码QAQ以后填坑!!
然后就要挂出这天早上考试时因为做的到最后束手无策而用鼠标在画图上艰难作的画叻!!魏无羡妈妈永远爱你!!