题意
题解
这题想了我一早上。。
看来还是我太菜了QAQ
但是感觉想出来的时候觉得他很简单嘛
设第i行的旗子数为
Hi
H
i
不难发现,答案和顺序是没有关系的,都是
Hi∗(Hi−1)/2
H
i
∗
(
H
i
−
1
)
/
2
的总和。。
于是我们稍作思考,发现一个很对的贪心策略
其实这题是这样的
现在有n个数,m个操作
这n个数一开始都是0
每个操作包含两个数,L,K (K≤L)
对于每一个操作,你需要将前L个数前k小的全部加1
如现在有个序列 1 2 3 4
操作为3 2
那么前3个数前k小的有1和2
所以出来的数为2,3,3,4
现在问你经过m次操作后每个数是多少
看不懂地可以看一下暴力的实现
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=10005;
int n;
struct qq
{
int h,k;
}s[N];
bool cmp (qq a,qq b){return a.h<b.h;}
int h[N];//现在这个杆上有多少
struct qt
{
int x,id;
}a[N];//用来找k大
bool cmp1 (qt a,qt b){return a.x<b.x;}
int main()
{
scanf("%d",&n);
for (int u=1;u<=n;u++) scanf("%d%d",&s[u].h,&s[u].k);
sort(s+1,s+1+n,cmp);
for (int u=1;u<=n;u++)
{
int cnt=0;
for (int i=1;i<=s[u].h;i++)
{
a[++cnt].x=h[i];
a[cnt].id=i;
}
sort(a+1,a+1+cnt,cmp1);
/*for (int u=1;u<=cnt;u++)
printf("%d %d\n",a[u].id,a[u].x);
printf("%d\n",s[u].k);*/
for (int i=1;i<=s[u].k;i++)
{
// printf("YES:%d\n",a[i].id);
h[a[i].id]++;
}
/*for (int u=1;u<=10;u++) printf("%d ",h[u]);
printf("\n");*/
}
int ans=0;
for (int u=1;u<=s[n].h;u++)
ans=ans+(h[u]-1)*h[u]/2;
printf("%d\n",ans);
return 0;
}
自己yy一下就知道这个模型很对
于是我就在这个模型上面卡了两三个小时才弄出来
怎么弄呢?
我们可以人为地使得这个序列递减
那么每一次要访问的区间就知道了,我们就使得这一段区间+1
但是你会发现这会有一个问题,就是+1之后可能就使得区间不递减了
那怎么办呢?
我们不难想到,使得区间不递减的情况只有下面这一个
也就是端点的两端是同一个数,别的情况都是没有影响的
这样我们不难想到一个方法,就是使得上图中,端点右端的a不加,找到a的最左端点加,这样就没有问题了,容易知道,最左边的a加是不会有任何影响的
至于怎么找,怎么维护,就都是线段树上的问题了,细节问题看看代码吧
是不是很简单,是不是觉得我想了一早上很菜?
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const LL N=100005*2;
const LL MAX=1<<30;
LL n;
struct qq
{
LL h,k;
}a[N];
bool cmp (qq a,qq b){return a.h<b.h;}
struct qt
{
LL l,r;
LL s1,s2;
LL minn;//这段区间的最小值 最大值
LL c;//区间加?
}s[N*2];LL num;
LL maxx;
void bt (LL l,LL r)
{
LL aa=++num;
s[aa].l=l;s[aa].r=r;
s[aa].c=0;
if (l==r)
{
s[aa].minn=0;
if (l==maxx) s[aa].minn=-MAX;
return ;
}
LL mid=(l+r)>>1;
s[aa].s1=num+1;bt(l,mid);
s[aa].s2=num+1;bt(mid+1,r);
s[aa].minn=min(s[s[aa].s1].minn,s[s[aa].s2].minn);
}
void push_down (LL now)
{
LL c=s[now].c;s[now].c=0;
LL s1=s[now].s1,s2=s[now].s2;
s[s1].minn+=c;s[s1].c+=c;
s[s2].minn+=c;s[s2].c+=c;
return ;
}
LL find (LL now,LL x)//我现在需要寻找第x个数
{
if (s[now].l==s[now].r) return s[now].minn;
push_down(now);
LL mid=(s[now].l+s[now].r)>>1;
LL s1=s[now].s1,s2=s[now].s2;
if (x<=mid) return find(s1,x);
else return find(s2,x);
}
LL find1 (LL now,LL x)//找到第一个小于等于这个数的位置
{
if (s[now].l==s[now].r)
return s[now].l;
push_down(now);
LL mid=(s[now].l+s[now].r)>>1;
LL s1=s[now].s1,s2=s[now].s2;
if (s[s1].minn<=x) return find1(s1,x);
else return find1(s2,x);
}
void change (LL now,LL l,LL r)
{
if (s[now].l==l&&s[now].r==r)
{
s[now].minn++;s[now].c++;
return ;
}
push_down(now);
LL s1=s[now].s1,s2=s[now].s2;
LL mid=(s[now].l+s[now].r)>>1;
if (r<=mid) change(s1,l,r);
else if (l>mid) change(s2,l,r);
else change(s1,l,mid),change(s2,mid+1,r);
s[now].minn=min(s[s1].minn,s[s2].minn);
}
LL ans=0;
void dfs (LL now)
{
if (s[now].l==s[now].r)
{
if (s[now].l==maxx) return ;
ans=ans+s[now].minn*(s[now].minn-1)/2;
return ;
}
push_down(now);
LL s1=s[now].s1,s2=s[now].s2;
dfs(s1);dfs(s2);
}
int main()
{
scanf("%lld",&n);
for (LL u=1;u<=n;u++)
scanf("%lld%lld",&a[u].h,&a[u].k);
sort(a+1,a+1+n,cmp);maxx=a[n].h+1;
bt(1,maxx);
for (LL u=1;u<=n;u++)//插入
{
LL t=a[u].h-a[u].k+1;//我们要的范围是哪里
LL ooo=find(1,t);//找到最前面的那个数
LL ooo1=find1(1,ooo-1);//找到这个东西的右端点
LL ooo2=find1(1,ooo);//找到这个东西的左端
LL lalal=min(a[u].k,ooo1-t);//看下有多少个数在这个范围里面
if (ooo1<=a[u].h) change(1,ooo1,a[u].h);
change(1,ooo2,ooo2+lalal-1);
}
dfs(1);
printf("%lld\n",ans);
return 0;
}