[bzoj4378] [POI2015]Logistyka
离线+树状数组。。
对于某个询问(S次,每次选出C个数),数列中每个数最多被减S次,只要判断(C- (数列中>=S的数个数))*S是否小于等于(数列中<S的数的和)就好了。
或者说。。把数列中>=S的数都变成S后,求一下总和是否大于等于S*C。。。
因为数字可能很大所以离线后hash一发。
网上的奇怪证明都是什么鬼。。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #define ll long long 6 using namespace std; 7 const int maxn=1000233; 8 struct zs{ 9 int id,v; 10 }b[maxn];int cnt; 11 struct ask{ 12 bool id;int x,y; 13 }q[maxn]; 14 int mp[maxn],t[maxn],c[maxn]; 15 ll sm[maxn],sum; 16 int i,j,k,n,m,num; 17 18 int ra;char rx; 19 inline int read(){ 20 rx=getchar(),ra=0; 21 while(rx<'0'||rx>'9')rx=getchar(); 22 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 23 } 24 inline void add(int x){ 25 // printf("add %d\n",x); 26 for(int i=x;i<=cnt;i+=i&-i)t[i]++,sm[i]+=c[x]; 27 } 28 inline void del(int x){ 29 for(int i=x;i<=cnt;i+=i&-i)t[i]--,sm[i]-=c[x]; 30 } 31 inline void query(int x,int &num,ll &sum){ 32 num=sum=0; 33 for(int i=x;i;i-=i&-i)num+=t[i],sum+=sm[i]; 34 } 35 36 37 bool cmp(zs a,zs b){return a.v<b.v;} 38 int main(){ 39 n=read(),m=read();char id; 40 for(i=1;i<=m;i++){ 41 for(id=getchar();id<'A'||id>'Z';id=getchar()); 42 q[i].id=id=='U'; 43 if(id=='U')q[i].x=read(),q[i].y=read(); 44 else q[i].x=read(),q[i].y=read(); 45 b[i].v=q[i].y,b[i].id=i; 46 } 47 sort(b+1,b+1+m,cmp); 48 for(i=1;i<=m;i++){ 49 if(b[i].v!=b[i-1].v||i==1)c[++cnt]=b[i].v; 50 q[b[i].id].y=cnt; 51 } 52 int n1=0; 53 for(i=1;i<=m;i++){ 54 // printf("%d %d %d\n",q[i].id,q[i].x,q[i].y); 55 if(q[i].id){ 56 // all-=c[mp[q[i].x]],all+=c[q[i].y]; 57 if(mp[q[i].x])del(mp[q[i].x]);else n1++; 58 add(q[i].y),mp[q[i].x]=q[i].y; 59 }else{ 60 query(q[i].y,num,sum);num=n1-num;//printf("number>=%d:%d rest:%lld\n",c[q[i].y],num,sum); 61 if(sum>=(ll)c[q[i].y]*(q[i].x-num))puts("TAK");else puts("NIE"); 62 } 63 } 64 return 0; 65 }