NOIP模拟赛 鸽巢原理 + 众数 + Trie

Set

Set(set.cpp/c/pas, 1s, 64MB)
题目描述
你手上有N个非负整数, 你需要在这些数中找出一个非空子集, 使得它的元素之和能被N整除. 如果有多组合法方案, 输出任意一组即可.
注意: 请使用高效的输入输出方式避免输入输出耗时过大.
输入格式
第一行一个整数N, 代表数字个数.
接下来一行N个数, 代表你手上的数.
输出格式
如果无解, 输出-1.
否则, 第一行输出一个整数M, 代表你选择的数的个数.
接下来一行M个数, 代表你选中元素的下标, 这M个数必须两两不同.
M <= 1000000

题解

鸽巢原理, 所以无-1之说… 研究会不会有-1的情况也是发现题目性质的好思路.
求出前缀和, 前缀和最多只有N个取值, 但是一共有S[0], S[1]..S[N] 这N+1个值, 所以一定有某
两个S[i], S[j]相等, 找出来再把i到j输出就可以了.

#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn = 1000005;
int n, st;
int vis[maxn], a[maxn];
long long sum;
inline const int read()
{
    register int x = 0;
    register char ch = getchar();
    while(ch < '0' || ch > '9') ch = getchar();
    while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x;
}
int main()
{
    freopen("set.in", "r", stdin);
    freopen("set.out", "w", stdout); 
    n = read();
    for (int i = 1; i <= n; ++ i) a[i] = read();
    for (int i = 1; i <= n; ++ i)
    {
        sum = (sum + a[i]) % n;
        if(!sum || vis[sum])
        {
            if(vis[sum]) st = vis[sum] + 1;
            else st = 1;
            printf("%d\n", i - st + 1);
            for (int j = st; j <= i; ++ j)
                printf("%d ", j);
            return 0;
        }
        vis[sum] = i;
    }
}

Read

Read(read.cpp/c/pas, 1s, 16MB)
题目描述
热爱看书的你有N本书, 第i本书的种类为A[i]. 你希望每天能够看一本书, 但是不希望连续两天看种类相同的书. 为了达成这个条件, 你需要选择一些书不看, 作为一个好学生, 你希望不看的书尽可能少, 求最少可以有多少书不看.
输入格式
为了避免输入文件过大, 我们采取如下方式生成A[i].
第一行读入两个个整数M, K.
接下来一行读入M个整数count[i], 其中 .
接下来一行读入M个整数X[i].
接下来一行读入M个整数Y[i].
接下来一行读入M个整数Z[i].
然后按照下面这段代码生成.
int N = 0, S = (1 << K) - 1;
for (int i = 1; i <= M; ++i) {
N = N + 1;
A[N] = X[i];
long long last = X[i];
for (int j = 1; j < count[i]; ++j) {
last = (last * Y[i] + Z[i]) & S;
N = N + 1;
A[N] = last;
}
}
注意:因为生成A[i]的方法不好用语言描述, 所以用代码来表达. 直接照搬代码的话后果 自负.
输出格式
输出一个整数表示答案.
N <= 50000000, M <= 1000, K <= 30

题解

可先看看bzoj2456 mode.
要想每天读书的种类都不同, 就要求每一种书的数目不超过其它书的数目+1. 所以只要看是否有一
种书超过了(N+1)/2. 本题空间限制很小, 但是N有很大, 所以不能用数组存下来, 但是我们只要找到超过
(N + 1) / 2的书, 所以我们用两个变量id, cnt, cnt初始为0. 然后生成每一个A[i], 如果cnt==0, 那么就
令id=A[i], cnt=1, 否则如果id==A[i], 则cnt++, 如果不等于, cnt–. 最后只要再扫一遍求出id的出现次
数即可.
因为如果有书超过(N+1)/2, 那么就以为这它比其他所有书的和都多, 那么cnt怎么减都不会小于
0, 如果没有那id随便是哪个都没有关系了. 最后再求这种书要减少多少个就可以了.

#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long lnt;
const int maxn = 1005;
int tot, m, K, cnt, n, ans, lim, cntt[maxn];
lnt now, mor, S;
lnt x[maxn], y[maxn], z[maxn];
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("%d", &cntt[i]), n += cntt[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]);

    S = (1 << K) - 1;
    for (int i = 1; i <= m; ++ i)
    {
        now = x[i];
        if(!tot || mor == now) ++ tot, mor = now;
        else --tot;
        for (int j = 1; j < cntt[i]; ++ j)
        {
            now = (now * y[i] + z[i]) & S;
            if(!tot || mor == now) ++ tot, mor = now;
            else --tot;
        }
    }
    for (int i = 1; i <= m; ++ i)
    {
        now = x[i];
        if(now == mor) ++ cnt;
        for (int j = 1; j < cntt[i]; ++ j)
        {
            now = (now * y[i] + z[i]) & S;
            if(mor == now) ++ cnt;
        }
    }
    if(n & 1) lim = n / 2 + 1;
    else lim = n / 2;
    if(cnt > lim)
        printf("%d\n", 2 * cnt - n - 1);
    else puts("0");
}
/*
7 2
1 1 1 1 1 1 1
1 1 1 2 2 1 1
0 0 0 0 0 0 0
0 0 0 0 0 0 0
*/

Race

Race(race.cpp/c/pas, 1s, 256MB)
题目描述
一年一度的运动会开始了. 有N个选手参赛, 第i个选手有一个能力值Ai, 比赛一共进行了 天. 在第j天( )的比赛中, 第i个选手的得分为A[i] xor j, 然后从大到小排名, 排名为x(x从0开始)的同学会获得 的积分, 你需要求出每个同学最后总的积分和q[i]模 的结果p[i]. 为了避免输出文件过大, 你只要输出p[i]的异或和即可.
输入格式
第一行三个整数, 分别代表N, M.
接下来一行N个整数, 第i个数代表A[i].
输出格式
一个整数代表答案.
样例
race.in
race.out
3 2
0 1 2
8
数据范围
对于10%的数据, M <= 5.
对于30%的数据, N <= 100.
对于50%的数据, N <= 1000.
对于100%的数据, N <= 200000, M <= 30, A[i] < 2 ^ M

题解

可先看看bzoj管道取珠.
考虑怎么求出x的答案. 平方相当于是枚举两个人(可以相同), 把这两个人同时排在x前面的天数
计入答案. 那么对于x, 如果我们求出f[i]也就是能力值的二进制中第i+1到M-1位都和他相等且第i位不
同的人有多少个, 那么这些人是否排在他前面只由第i位确定, 一共有2^(M-1)天, 而且不需要从这些人中
枚举两个人了, 因为直接平方即可. 我们只要枚举i和j, f[i]这些人和f[j]这些人同时排在他前面的天数为
2^(M-2), 所以把2 * f[i]*f[j]*2^(M-2)计入答案. 具体实现有很多方法, 可以用trie树实现.

#include<stdio.h>
typedef long long lnt;
const int mod = 1e9 + 7; 
const int maxn = 2e5 + 5;
lnt ans, lim;
int n, m, tot;
int ch[maxn * 30][2], siz[maxn * 30];
inline const int read()
{
    register int x = 0;
    register char ch = getchar();
    while(ch < '0' || ch > '9') ch = getchar();
    while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x;
}
inline void insert(int x)
{
    int p = 0;
    for (int i = m - 1; ~i; -- i)
    {
        int wh = (x >> i) & 1;
        if(!ch[p][wh]) ch[p][wh] = ++ tot;
        p = ch[p][wh];
        ++ siz[p];
    }
}
void dfs(int u, lnt sum, lnt num)
{
    if (!ch[u][0] && !ch[u][1])
    {
        ans ^= sum;
        return;
    }
    lnt tmp;
    tmp = siz[ch[u][1]] * (siz[ch[u][1]] + num) % mod * lim % mod;
    if (ch[u][0]) dfs(ch[u][0], (sum + tmp) % mod, num + siz[ch[u][1]]);

    tmp = siz[ch[u][0]] * (siz[ch[u][0]] + num) % mod * lim % mod;
    if (ch[u][1]) dfs(ch[u][1], (sum + tmp) % mod, num + siz[ch[u][0]]); 
}
int main()
{
    freopen("race.in", "r", stdin);
    freopen("race.out", "w", stdout);
    n = read(), m = read();
    lim = 1 << (m - 1);
    int x;
    for (int i = 1; i <= n; ++ i) x = read(), insert(x);
    dfs(0, 0, 0);
    printf("%lld\n", ans);
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值