[百炼智能]hihoCoder挑战赛37 D Items(树状数组维护01背包—梦想成真!!!)
http://hihocoder.com/contest/challenge37/problem/4
-
考虑每次都是把一段或到另一段去。
-
也就是说要快速找到原来段1,新的段0的位置
-
结论:(1,0)的个数=(0,1)的个数
-
证明:设增量是\(x\),\(a[i]\)到\(a[(i+x)~mod~m]\)连边,可以发现若有\((0,1)\),则迟早会出现\((1,0)\)
-
那么只要找到不同的位置,树状数组维护hash值,每次二分lcp即可,复杂度\(O(m~log^2~m)\)
Code:
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i < _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;
const ll mo = 23333333333333333ll;
ll mul(ll x, ll y) {
ll z = (long double) x * y / mo;
z = x * y - z * mo;
if(z < 0) z += mo; else if(z >= mo) z -= mo;
return z;
}
const int W = 29;
const int N = 3e5 + 5;
ll w[N];
int n, m, x;
#define low(x) ((x) & -(x))
ll f[N];
void add(int x, ll y) {
x ++;
for(; x <= m; x += low(x)) f[x] = (f[x] + y) % mo;
}
ll sum(int x) {
x ++;
ll s = 0;
for(; x; x -= low(x)) s += f[x];
return (s % mo + mo) % mo;
}
int d[N], d0, a[N];
int pd(int x, int y, int l, int r) {
if(x > l) swap(x, l), swap(y, r);
ll s1 = (sum(y) - sum(x - 1) + mo) % mo;
ll s2 = (sum(r) - sum(l - 1) + mo) % mo;
return mul(s1, w[l - x]) == s2;
}
int ef(int p, int q) {
if(p > q) swap(p, q);
int as = 0;
for(int l = 1, r = m - q; l <= r; ) {
int d = l + r >> 1;
if(pd(p, p + d - 1, q, q + d - 1)) as = d, l = d + 1; else r = d - 1;
}
return as;
}
void solve(int x, int y, int l, int r) {
if(x > y) return;
while(x <= y && l <= r) {
int g = ef(x, l);
if(x + g - 1 >= y) return;
d[++ d0] = l + g;
x += g + 1, l += g + 1;
}
}
int main() {
scanf("%d %d", &n, &m);
w[0] = 1; fo(i, 1, m) w[i] = w[i - 1] * W % mo;
a[0] = 1; add(0, w[0]);
fo(ii, 1, n) {
scanf("%d", &x);
x %= m;
d0 = 0;
solve(0, m - x - 1, x, m - 1);
solve(m - x, m - 1, 0, x - 1);
fo(i, 1, d0) {
x = d[i];
if(!a[x]) {
a[x] = 1;
add(x, w[x]);
}
}
}
int Q; scanf("%d", &Q);
fo(ii, 1, Q) {
scanf("%d", &x);
pp("%s\n", a[x] ? "YES" : "NO");
}
}
转载注意标注出处:
转自Cold_Chair的博客+原博客地址