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
2len−1>=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]=1且s[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;
}