2021GDCPC (B,K)
B
思路
看时间复杂度肯定是预处理o1查询,每个数字只能选择一次,可以使用01背包求解
f[i]+=f[i-a[j]*a[j],枚举i时,使用一个变量max,表示上一层枚举到的最大值,如果使用N的话其实好多f[i]并没有更新
代码:
int fib[N];
int f[N];
void init() {
fib[0] = fib[1] = 1;
f[0] = 1;
for (int i = 2;i <= 36;i++) fib[i] = fib[i - 1] + fib[i - 2];
int mmax=0;
for (int i = 0;i <= 35;i++) {
mmax=min(N-5,mmax+fib[i]);
for (int j = mmax;j >= fib[i];j--) {
f[j] += (f[j - fib[i]] * fib[i]) % mod;
f[j] %= mod;
}
}
}
void solve() {
int n = read();
printf("%lld\n", f[n]);
clean();
}
K
题意:
给定n个线段,查询l到r之间完全覆盖的线段的最大值减最小值
思路:
可以尝试在l这个点上建立一个高度为r-l+1的点,并且这个点的值为val,所求的值就是l到r每个列最高高度为r的条形中的最值,
可以使用二维树状数组维护,然后把l
变成N-l
,相当于把小于l的点全放到右边,大于的点放到左边,这样所求出来的前缀的最值就是结果,
PS:查询(l,r)的前缀会查询到L>l,但是r<L,所以对答案不会产生贡献
代码:
const int N = 3030, mod = 998244353;
int cmax[N][N], cmin[N][N];
#define lowbit(x) x&(-x)
void add(int l, int r, int val) {
l = N - l;
for (;l < N;l += lowbit(l)) {
int pr = r;
for (;pr < N;pr += lowbit(pr)) {
cmax[l][pr] = max(cmax[l][pr], val);
cmin[l][pr] = min(cmin[l][pr], val);
}
}
}
int ask(int l, int r) {
l = N - l;
int mmax = 0, mmin = 1e18;
for (;l;l -= lowbit(l)) {
int pr = r;
for (;pr;pr -= lowbit(pr)) {
mmax = max(mmax, cmax[l][pr]);
mmin = min(mmin, cmin[l][pr]);
}
}
return mmax - mmin;
}
void solve() {
int n = read(), m = read();
memset(cmax, 0xcf, sizeof cmax);
memset(cmin, 0x3f, sizeof cmin);
for (int i = 1;i <= n;i++) {
int l = read(), r = read(), val = read();
add(l, r, val);
}
int lst = 0;
for (int i = 1;i <= m;i++) {
int op = read(), l = read(), r = read();
l ^= lst, r ^= lst;
if (l > r) swap(l, r);
if(op==1){
int val=read();
add(l,r,val);
continue;
}
int res = ask(l, r);
printf("%lld\n", res);
lst = res;
}
clean();
}