假如我們已知第i天可以購買的所有商品,那麼剩下就是01揹包的問題了.能夠購買的所有商品由兩個條件決定:編號下標和距離.我們可以減少一個維度使問題更簡單.
假如把詢問和點一起按照距離d排序,那麼就能保證詢問到i時,所有考慮到的點x一定滿足
現在問題就轉化成了:
詢問:在
更新:點x的價值爲
對於區間問題,可以考慮線段樹求解.既然詢問價值和的可能性,而且
對於單點更新x操作:
對每個經過x的區間用
對區間查詢
用
最終的複雜度爲:
這裏有一個大優化!
對於單點更新x操作:
對於更新前 每個的
假如我們把h數組看成一個01串,那麼
對於區間合併:
假設當前區間的01串爲a1,兩個兒子的01串分別爲a2,a3.
對於a3的每個值爲1的位i:
那有什麼能夠高效地完成以上二進制的操作?!
bitset!!
它相當於把64位的數字接在一起,形成一個01串,並且支持二進制的所有運算,比如按位或,位移等.
複雜度:
用bitset完成操作後,時間複雜度爲:
當然這種思路有一個神奇的解法:
可用BIT求一個區間內每個商品的個數.
商品的種類最多隻有100種,而每種商品的數量卻很多,這種情況可以選擇多重揹包進行優化.把物品總數減少到
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<bitset>
using namespace std;
const int M=105;
int n,m;
int ans[100005];
int id=0,l,r,c,sum;
bitset<M>dp;
inline void rd(int &res){
char c;res=0;
while(c=getchar(),c<48);
do res=(res<<1)+(res<<3)+(c^48);
while(c=getchar(),c>47);
}
struct node{
int v,d,id;
}A[20005];
struct LZ{
int l,r,d,sum,id;
}Q[100005];
int bit[20005][105];
void add(int x,int y){
while(x<=n){
bit[x][y]++;
x+=x&-x;
}
}
int query(int x,int y){
int sum=0;
while(x){
sum+=bit[x][y];
x^=x&-x;
}
return sum;
}
bool cmp(node a,node b){return a.d<b.d;}
bool cmp1(LZ a,LZ b){return a.d<b.d;}
int val[20005];
int main(){
int cas;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)rd(A[i].v);
for(int i=1;i<=n;i++)rd(A[i].d),A[i].id=i;
sort(A+1,A+n+1,cmp);
for(int i=1;i<=m;i++){
ans[i]=0;
rd(Q[i].l);
rd(Q[i].r);
rd(Q[i].d);
rd(Q[i].sum);Q[i].id=i;
}
sort(Q+1,Q+m+1,cmp1);
int x=1;
for(int i=1;i<=m;i++){
while(x<=n&&A[x].d<=Q[i].d){
add(A[x].id,A[x].v);
x++;
}
int can=query(Q[i].r,Q[i].sum)-query(Q[i].l-1,Q[i].sum);
if(can){ans[Q[i].id]=0;}
else{
dp.reset();
dp[0]=1;
int cnt=0;
for(int j=1;j<Q[i].sum;j++){
int s=query(Q[i].r,j)-query(Q[i].l-1,j),p=1;
while(p<s){
val[++cnt]=p*j;
s-=p;
p<<=1;
}
if(s)val[++cnt]=s*j;
}
for(int j=1;j<=cnt;j++){
dp|=dp<<val[j];
if(dp[Q[i].sum])break;
}
if(!dp[Q[i].sum])ans[Q[i].id]=1;
}
}
for(int i=1;i<=m;i++)putchar(ans[i]^48);
return 0;
}
當然最穩(qi)定(pa)的正解的思路是分治!
官方題解傳送門: http://bestcoder.hdu.edu.cn/
對於
那麼現在只要回答左端點在左區間,右端點在右區間的詢問.
假設
可以用
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=1e5+5;
const int N=2e4+5;
const int S=105;
const int oo=1e9+5;
struct node{
int id,l,r,sum,d;
}Q[M];
inline void rd(int &res){
res=0; char c;
while (c=getchar(),c<48);
do{
res=(res<<3)+(res<<1)+(c^48);
}while (c=getchar(),c>=48);
}
bool cmp(node a,node b){
if(a.l!=b.l)return a.l<b.l;
return a.r<b.r;
}
int dp[S][N],res[M],val[N],n,m,dis[N],q[M],st[N];
void solve(int L,int R){//Q.l<=mid;Q.r>=mid
int mid=L+R>>1,i,j,tot=0;
if(L==R){
for(i=st[L];i<=st[R+1]-1;i++){
if(Q[i].r==Q[i].l&&Q[i].l==L)q[++tot]=i;
}
for(i=1;i<=tot;i++){
int id=q[i];
if(Q[id].d>=dis[L]&&Q[id].sum==val[L])res[Q[id].id]=0;
else res[Q[id].id]=1;
}
return ;
}
for(i=st[L];i<=st[mid+1]-1;i++){
if(Q[i].r>mid&&Q[i].r<=R)q[++tot]=i;
}
if(tot){
for(i=L;i<=R;++i){
for(j=1;j<S;++j)dp[j][i]=oo;
dp[0][i]=0;
}
dp[val[mid]][mid]=dis[mid];
dp[val[mid+1]][mid+1]=dis[mid+1];
for(i=mid-1;i>=L;--i){//dp[i][j]表示[i,mid]區間中,得到j 最遠距離的最小值
for(j=0;j<S;++j){
dp[j][i]=dp[j][i+1];
if(j>=val[i])dp[j][i]=min(dp[j][i],max(dp[j-val[i]][i+1],dis[i]));
}
}
for(i=mid+2;i<=R;++i){
for(j=0;j<S;++j){
dp[j][i]=dp[j][i-1];
if(j>=val[i])dp[j][i]=min(dp[j][i],max(dp[j-val[i]][i-1],dis[i]));
}
}
for(i=1;i<=tot;++i){//橫跨mid的詢問
int id=q[i];
int mi=oo,l=Q[id].l,r=Q[id].r,sum=Q[id].sum;
for(j=0;j<=Q[id].sum;++j){
mi=min(mi,max(dp[j][l],dp[sum-j][r]));
}
if(mi<=Q[id].d)res[Q[id].id]=0;
else res[Q[id].id]=1;
}
}
solve(L,mid);
solve(mid+1,R);
}
int find(int x){//Q[i].l>=x
int l=1,r=m,res=m+1;
while(l<=r){
int mid=l+r>>1;
if(Q[mid].l>=x){
r=mid-1;
res=mid;
}else l=mid+1;
}return res;
}
int main(){
int i,j,k,cas,a,b,c;
rd(n);rd(m);
for(i=1;i<=n;i++)rd(val[i]);
for(i=1;i<=n;i++)rd(dis[i]);
for(i=1;i<=m;i++){
rd(Q[i].l);rd(Q[i].r);rd(Q[i].d);rd(Q[i].sum);
Q[i].id=i;
}
sort(Q+1,Q+1+m,cmp);
for(i=1;i<=n;i++){//st[i]表示L>=i的第一個詢問的下標
st[i]=find(i);
}
st[n+1]=m+1;
solve(1,n);
for(i=1;i<=m;i++){
if(res[i])putchar('1');
else putchar('0');
}
puts("");
return 0;
}