luogu P5527 [Ynoi2012] NOIP2016 人生巅峰

https://www.luogu.com.cn/problem/P5527

不算很难
先看数据范围, v < = 1000 v<=1000 v<=1000发现问题不对
yy一下发现询问区间越长,越容易重复(鸽巢原理)
没有交集肯定是没用的,因为如果有交集就把那部分都减掉就行了(具有可减性)
列出式子
2 l e n − 1 > = 1000 l e n 2^{len}-1>=1000len 2len1>=1000len
大概可以得到当 l e n > = 13 \large len>=13 len>=13的时候是一定可以找到重复的
那考虑13以下怎么做
把区间从前往后考虑,记录 s [ j ] s[j] s[j]表示用前 i i i个数是否能组成 j j j,新加一个数直接像做背包一样扔进去就行了
假设新加的数是 V V V,强制选出来的集合中有一个包含 V V V,那么如果 s [ j ] = 1 且 s [ j − ( V + 1 ) ] = 1 s[j]=1且s[j-(V+1)]=1 s[j]=1s[j(V+1)]=1就是有重复的
显然正确
然后把这个背包用bitset优化一下就行了
3次方那里可以直接用一个树状数组维护被修改了几次,然后预处理倍增计算即可
code:

#include<bits/stdc++.h>
#define N 100050
#define M 13005
#define lowbit(x) (x & -x)
using namespace std;
int t[N], n;
void update(int x, int y) {
    for(; x <= n; x += lowbit(x)) t[x] += y;
}
int query(int x) {
    int ret = 0;
    for(; x; x -= lowbit(x)) ret += t[x];
    return ret;
}
int m, mod, a[N], f[M][20];
bitset<M> S;
int main() {
    scanf("%d%d%d", &n, &m, &mod);
    for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
    for(int i = 0; i < mod; i ++) f[i][0] = 1ll * i * i * i % mod;
    for(int j = 1; j <= 18; j ++)
        for(int i = 1; i < mod; i ++)
            f[i][j] = f[f[i][j - 1]][j - 1];
    
    while(m --) {
        int o, l, r;
        scanf("%d%d%d", &o, &l, &r);
        if(o == 2) update(l, 1), update(r + 1, -1);
        else {
            if(r - l + 1 > 13) printf("Yuno\n");
            else {
                int ff = 0; S.reset(); S[0] = 1;
                for(int i = l; i <= r; i ++) {
                    int x = a[i], tg = query(i);
                    for(int j = 18; j >= 0; j --) if((tg >> j) & 1) x = f[x][j];
                    x ++;
                    if((S & (S << x)).any()) {ff = 1; break;}
                    S |= (S << x);
                }
                if(ff) printf("Yuno\n");
                else printf("Yuki\n");
            }
        }
    }
    return 0;
}
posted @ 2021-08-30 15:24  lahlah  阅读(48)  评论(0编辑  收藏  举报