砝码称重 题解
砝码称重 题解
前言
这道题时限完全可以开到 1s,空间也开不到 1024kb
白想那么多优化(
不过这个复杂度可能是目前来看最合理(算出来保证能过)的。
题意简述
有一个长度为
- 把
到 的所有数改为 ; - 查询用
到 的所有数(每个数可以用无数次)能否通过相加得到 。
保证
思路
首先看到这个不超过
然后就是一个背包了。我们设
当然,这里对于每一个
直接枚举倍数感觉非常过不去这里给出第一个优化。我们发现这里的转移很多都是重复的,比如用
然后还是感觉很寄我们发现每次的转移相当于是把数组整体右移
然后我们其实就可以枚举集合,然后用集合内的数来求
然后我们发现,很多转移还是冗余的,因为在我们求新的集合
线段树自带的复杂度是
后记
其实 dp 部分我想麻烦了,并不需要枚举每个倍数,只需要向前转移就行,这样在枚举到 所以我的复杂度还是少一半。
代码:
#include<bits/stdc++.h> using namespace std; const int N = 1e6+10, M = 1030, MM = 1e5+10; int read() { int x = 0; char ch = getchar(); while(ch<'0' || ch>'9') ch = getchar(); while(ch>='0'&&ch<='9') x = x*10+(ch-48), ch = getchar(); return x; } bitset<100010> f[1030]; struct Question{ int op, l, r, x; }q[N]; int n, m, Q; int vis[MM];//每个数的编号 int a[N]; int nums[12], totn; struct node{ int st;//记录区间内的集合 int tag;//标记被改成了哪个集合 }; struct segmentTree{ node tree[N<<2]; #define ls tr<<1 #define rs tr<<1 | 1 void push_up(int tr) { tree[tr].st = (tree[ls].st | tree[rs].st); } void push_down(int tr) { if(tree[tr].tag) { tree[ls].st = tree[rs].st = tree[ls].tag = tree[rs].tag = tree[tr].tag; tree[tr].tag = 0; } } void build(int tr, int L, int R) { if(L == R) { tree[tr].st = (1<<(vis[a[L]] - 1)); return; } int mid = (L+R)>>1; build(ls, L, mid); build(rs, mid+1, R); push_up(tr); } void modify(int tr, int L, int R, int lq, int rq, int val) { if(lq <= L && R <= rq) { tree[tr].st = tree[tr].tag = val; return; } push_down(tr); int mid = (L+R)>>1; if(lq <= mid) modify(ls, L, mid, lq, rq, val); if(mid < rq) modify(rs, mid+1, R, lq, rq, val); push_up(tr); } int query(int tr, int L, int R, int lq, int rq) { if(lq <= L && R <= rq) { return tree[tr].st; } push_down(tr); int mid = (L+R)>>1, ret = 0; if(lq <= mid) ret|=query(ls, L, mid, lq, rq); if(mid < rq) ret|=query(rs, mid+1, R, lq, rq); return ret; } }seg; int main() { // cout << sizeof(f) << endl; n = read(), m = read(), Q = read(); for(int i = 1; i<=n; ++i) { a[i] = read(); if(!vis[a[i]]) { vis[a[i]] = ++totn; nums[totn] = a[i]; } } for(int i = 1; i<=Q; ++i) { q[i].op = read(), q[i].l = read(), q[i].r = read(), q[i].x = read(); if(q[i].op == 1) { if(!vis[q[i].x]) { vis[q[i].x] = ++totn; nums[totn] = q[i].x; } } } seg.build(1, 1, n); f[0][0] = 1; for(int s = 1; s<=(1<<totn)-1; ++s) { // f[s][0] = 1; int pos = 0; for(int i = totn; i>=1; --i) { if((s>>(i-1)) & 1) { //// int x = nums[i]; // for(int j = nums[i]; j<=m; j*=2) { // f[s] |= (f[s]<<j); // }//旧的转移 pos = i;//找到最高位在哪里 break; } } int ts = (s ^ (1<<(pos-1))); f[s] = f[ts]; for(int j = nums[pos]; j<=m; j*=2) { f[s] |= (f[s] << j); } } for(int i = 1; i<=Q; ++i) { if(q[i].op == 1) { seg.modify(1, 1, n, q[i].l, q[i].r, 1<<(vis[q[i].x] - 1)); } else { int st = seg.query(1, 1, n, q[i].l, q[i].r); if(f[st][q[i].x]) { puts("Yes"); } else { puts("No"); } } } return 0; }