[IOI2007] sails 船帆
线段树或者其他数据结构维护贪心
分析问题,其实就是要合理安排旗子使得每一行的旗子个数较平均,答案就是\(\sum{cnt[i]*(cnt[i]-1)/2}\)
考虑高度较低的旗杆放旗子比较不灵活(?),所以我们先让较低的放,不齐的由较高的旗杆补
对于\(h,k\),我们按照\(h\)递增排序,问题转化为对于\(1-h\)中最小的\(k\)个位置放旗子
显然不能暴力找最小的\(k\)个位置,但是我们可以在处理的过程中保证旗子的个数在\(1-h\)上呈递减,这样就不用考虑最小的k个难找了
如何保证递减呢?
因为每次操作只会改变\(1\),所以我们先找到\(h-k+1,h\)中最小的值\(x\),以及这个值出现的最左、右位置\(L,R\)
只要将\([R+1,h],[L,L+(k-(h-R))+1]\)这两部分+1,即可保证单调性
#include
using namespace std;
#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
inline void cmax(int &a,int b){ ((ab)&&(a=b));}
char IO;
int rd(){
int s=0,f=0;
while(!isdigit(IO=getchar())) f|=(IO=='-');
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
const int N=1e5+10;
const int R=1e5;
int n;
struct Node{
int h,k;
bool operator < (const Node __) const {
return h<__.h;
}
} A[N];
int s[N<<2],t[N<<2];
void Down(int p) {
if(!t[p]) return;
t[p<<1]+=t[p],t[p<<1|1]+=t[p];
s[p<<1]+=t[p],s[p<<1|1]+=t[p];
t[p]=0;
}
void Upd(int p,int l,int r,int ql,int qr,int x){
if(ql>qr) return;
if(ql==l&&qr==r) {
t[p]+=x;
s[p]+=x;
return;
}
Down(p);
int mid=(l+r)>>1;
if(qr<=mid) Upd(p<<1,l,mid,ql,qr,x);
else if(ql>mid) Upd(p<<1|1,mid+1,r,ql,qr,x);
else Upd(p<<1,l,mid,ql,mid,x),Upd(p<<1|1,mid+1,r,mid+1,qr,x);
s[p]=max(s[p<<1],s[p<<1|1]);
}
int Que1(int p,int l,int r,int x) {
do {
int mid=(l+r)>>1;
Down(p);
x<=mid?(p=p<<1,r=mid):(p=p<<1|1,l=mid+1);
} while(l!=r);
return s[p];
}
int Que2(int p,int l,int r,int x) {
do {
int mid=(l+r)>>1;
Down(p);
if(s[p<<1|1]>x) p=p<<1|1,l=mid+1;
else p=p<<1,r=mid;
} while(l!=r);
return s[p]<=x?1:l+1;
}
int main(){
rep(i,1,n=rd()) A[i].h=rd(),A[i].k=rd();
sort(A+1,A+n+1);
rep(i,1,n) {
int x=Que1(1,1,R,A[i].h-A[i].k+1);
int l=Que2(1,1,R,x-1),r=Que2(1,1,R,x);
Upd(1,1,R,l,A[i].h,1);
Upd(1,1,R,r,r+A[i].k-max(0,(A[i].h-l+1))-1,1);
}
ll ans=0;
rep(i,1,R) {
int x=Que1(1,1,R,i);
ans+=1ll*x*(x-1)/2;
}
printf("%lld\n",ans);
}