正题
题目链接:https://www.luogu.com.cn/problem/P3586
题目大意
nnn个数,有操作
- 修改一个数
- 假如每次选出ccc个正数让它们减去111,求能否进行操作sss次。
解题思路
如果有cntcntcnt个数大于sss那么有解当且仅当满足cnt≥ccnt\geq ccnt≥c或者小于sss的数的和大于(c−cnt)∗s(c-cnt)*s(c−cnt)∗s。
证明:第一个条件显然是充分的,考虑第二个条件。我们这cntcntcnt个数会被选择,在剩下的数中显然最优的话我们每次会选择最大的数来减去,那么发现这样每个数都会被取完知道剩下的数不到c−cntc-cntc−cnt个。
用动态开点的权值线段树维护即可,时间复杂度O(mlogai)O(m\log a_i)O(mlogai)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1e7+10,inf=1e9;
ll n,m,cnt,rt,val[N],a[N],sum,w,ls[N],rs[N],num[N];
void Change(ll &x,ll l,ll r,ll pos,ll z){if(!x)x=++cnt;val[x]+=z;num[x]+=z*pos;if(l==r)return;ll mid=(l+r)>>1;if(pos<=mid)Change(ls[x],l,mid,pos,z);else Change(rs[x],mid+1,r,pos,z);return;
}
ll Ask(ll x,ll l,ll r,ll pos){if(l==r){w+=num[x];return val[x];}ll mid=(l+r)>>1;if(pos<=mid){w+=num[rs[x]];return Ask(ls[x],l,mid,pos)+val[rs[x]];}return Ask(rs[x],mid+1,r,pos);
}
int main()
{freopen("book.in","r",stdin);freopen("book.out","w",stdout);scanf("%lld%lld",&n,&m);while(m--){char s[5];ll l,r;scanf("%s%lld%lld",s,&l,&r);if(s[0]=='U'){sum-=a[l]-r;if(a[l])Change(rt,1,inf,a[l],-1);Change(rt,1,inf,a[l]=r,1);}else{w=0;ll cnt=Ask(rt,1,inf,r);if(cnt>=l||sum-w>=(l-cnt)*r)printf("TAK\n");else printf("NIE\n"); }}return 0;
}