NOIP模拟题 2017.11.6

 


  题目大意 给定一个大小为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

  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 }
read (Random I)

Solution 2 (抽样法II)

  抽取每一段前10个,求出它们的众数,然后再带回原序列中求出现次数。

  显然数据很水所以过了,然后求众数时,我没有排序,还是过了(这。。。)

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[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 }
read (Random II)

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 }

转载于:https://www.cnblogs.com/yyf0309/p/7794761.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值