OJ題號
簡單題意
給 n 個數,前 i−1 個至少要把幾個變成 0 才能使前 i 項的和<= m。
正解思路
思路來自:https://blog.csdn.net/Ratina/article/details/97798320
對於每一個a[i],前綴和sum[i],
- 當sum[i]≤m,則ans[i]=0;
- 當sum[i]>m[i],我們就要刪除1 ~ i-1的元素(變爲0),爲了刪除的個數最少,很明顯要優先刪除大的元素,但暴力排序肯定不行,這裏就要構建一棵權值線段樹,找到構造成sum-m的最少元素。
對於權值線段樹的建立,我們把a[i]離散化後構建權值線段樹,但裏面存儲的是權值的個數和該權值的總和。
查詢的時候就是查找sum-m最少是多少個權值相加,利用二分的思想在線段樹上查找,大的值優先所以優先查找右子樹。鎖定分界點位置後依然需要用記錄個數的權值線段樹得到ans[i]。
代碼
#include<bits/stdc++.h>
#define LL long long
#define PLL pair<LL,LL>
using namespace std;
const int maxn=2e5+50;
int n,N;
LL m,w[maxn],b[maxn];
LL s[maxn<<2],c[maxn<<2];
void init()
{
sort(b+1,b+n+1);
N=unique(b+1,b+n+1)-(b+1);
for(int i=1; i<=n; i++)
w[i]=lower_bound(b+1,b+N+1,w[i])-b;
}
void updata(int rt,int l,int r,int p)
{
if(l==r)
{
s[rt]+=b[l];
c[rt]++;
return;
}
int mid=(l+r)>>1;
if(p<=mid)
updata(rt<<1,l,mid,p);
else
updata(rt<<1|1,mid+1,r,p);
s[rt]=s[rt<<1]+s[rt<<1|1];
c[rt]=c[rt<<1]+c[rt<<1|1];
}
PLL query1(int rt,int l,int r,LL k)
{
if(l==r)
{
if(k%b[l]==0)
return PLL(l,k/b[l]);
else
return PLL(l,k/b[l]+1);
}
int mid=(l+r)>>1;
if(s[rt<<1|1]>=k)
return query1(rt<<1|1,mid+1,r,k);
else
return query1(rt<<1,l,mid,k-s[rt<<1|1]);
}
LL query2(int rt,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)
return c[rt];
if(r<ql||l>qr)
return 0;
int mid=(l+r)>>1;
return query2(rt<<1,l,mid,ql,qr)+query2(rt<<1|1,mid+1,r,ql,qr);
}
int main()
{
int Q;
scanf("%d",&Q);
while(Q--)
{
scanf("%d %lld",&n,&m);
for(int i=1; i<=n; i++)
{
scanf("%lld",&w[i]);
b[i]=w[i];
}
init();
memset(s,0,sizeof(s));
memset(c,0,sizeof(c));
LL sum=0,ans[maxn];
for(int i=1; i<=n; i++)
{
sum+=b[w[i]];
if(sum>m)
{
PLL temp=query1(1,1,N,sum-m);
//cout<<temp.first<<" "<<temp.second<<endl;
ans[i]=temp.second;
if(temp.first+1<=m)
ans[i]+=query2(1,1,N,temp.first+1,m);
}
else
ans[i]=0;
updata(1,1,N,w[i]);
}
for(int i=1; i<=n; i++)
printf("%lld ",ans[i]);
printf("\n");
}
return 0;
}
結構體
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 2e5+10;
#define lson i<<1
#define rson i<<1|1
#define LS l,mid,lson
#define RS mid+1,r,rson
#define ll long long
#define N 200005
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define EXP 1e-8
#define lowbit(x) (x&-x)
ll a[N],b[N],b_n,n,m,ans[N];
ll ans_sum,ans_max,ans_min;
//注意一定要找一個臨時變量記錄下ans_***的答案,不然會覆蓋
struct node
{
int l,r;
ll sum,num;
} tree[N<<2];
void pushup(int i)
{
tree[i].sum=tree[lson].sum+tree[rson].sum;
tree[i].num=tree[lson].num+tree[rson].num;
}
//建立線段樹
void build(int l,int r,int i)
{
tree[i].l = l;
tree[i].r = r;
if(l == r)
{
tree[i].sum = 0;
tree[i].num = 0;
return;
}
int mid = (l+r)>>1;
build(LS);
build(RS);
pushup(i);
}
void add_data(int i,int l,int r,int pos)
{
if(l==r)
{
tree[i].sum += b[l];
tree[i].num++;
return;
}
int mid = (l+r)>>1;
if(pos<=mid)
add_data(lson,l,mid,pos);
else
add_data(rson,mid+1,r,pos);
pushup(i);
}
pair<ll,ll> query1(int i,int l,int r,ll val)
{
//cout<<l<<" "<<r<<" "<<i<<endl;
if(l == r)
{
if(val%b[l]==0)
return pair<ll,ll>(l,val/b[l]);
else
return pair<ll,ll>(l,val/b[l]+1);
}
int mid=(l+r)>>1;
if(tree[i<<1|1].sum>=val)
return query1(i<<1|1,mid+1,r,val);
else
return query1(i<<1,l,mid,val-tree[i<<1|1].sum);
}
LL query2(int i,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)
return tree[i].num;
if(r<ql||l>qr)
return 0;
int mid=(l+r)>>1;
return query2(i<<1,l,mid,ql,qr)+query2(i<<1|1,mid+1,r,ql,qr);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%lld%lld",&n,&m);
for(int i=1; i<=n; i++)
{
scanf("%lld",&a[i]);
b[i]=a[i];
}
sort(b+1,b+n+1);
b_n=unique(b+1,b+n+1)-(b+1);
for(int i=1; i<=n; i++)
{
a[i]=lower_bound(b+1,b+b_n+1,a[i])-b;
}
build(1,b_n,1);
ll sum=0;
for(int i=1; i<=n; i++)
{
sum+=b[a[i]];
if(sum<=m)
{
ans[i]=0;
}
else
{
pair<ll,ll> temp=query1(1,1,b_n,sum-m);
//cout<<temp.first<<" "<<temp.second<<" "<<tree[1].sum<<endl;
ans[i]=temp.second;
if(temp.first+1<=m)
{
ans[i]+= query2(1,1,b_n,temp.first+1,m);
}
}
add_data(1,1,b_n,a[i]);
}
for(int i=1;i<n;i++)
{
printf("%lld ",ans[i]);
}
printf("%lld\n",ans[n]);
}
return 0;
}