题解:CF627E Orchestra

传送门

首先容易想到一个枚举上下行从左往右扫列然后拿双指针维护一下的做法,考虑优化。

会发现只有枚举到有中提琴手的列才会让记录目前照片右边界可行的最靠左位置的指针向右移,于是只需关心这些列。

思考一下如何让复杂度带 \(k\)。会发现如果从矩形中删去一个点最多只会影响到 \(k\) 列所对应的指针,并且这些指针最多向右移一位,这里的移一位指的是到下一个有中提琴手的列。

于是可以先去计算照片下边界是第 \(r\) 行的情况,然后从下往上枚举行删点,拿链表维护即可。

CODE:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m,s,K;
int sx[3010],sy[3010];
struct aaa{int pre,nex,sum,cnt,ss;}a[3010];
int v[3010];
vector<int> ff[3010];
ll ans=0;
int main()
{
	scanf("%d%d%d%d",&n,&m,&s,&K);
	for(register int i=1;i<=s;i++) scanf("%d%d",&sx[i],&sy[i]),ff[sx[i]].push_back(sy[i]);
	for(register int i=1;i<=n;i++)
	{
		for(register int j=1;j<=m;j++) v[j]=0;
		for(register int j=1;j<=s;j++) if(sx[j]>=i) v[sy[j]]++;
		int la=0;
		for(register int j=1;j<=m;j++) if(v[j]) a[j].pre=la,a[la].nex=j,a[j].sum=v[j],la=j;
		a[la].nex=m+1,a[m+1].pre=la;
		for(register int j=1;j<=m;j++)
		{
			if(!v[j]) continue;
			int pos=j;a[j].cnt=0,a[j].ss=m+1;
			while(pos!=m+1)
			{
				a[j].cnt+=a[pos].sum;
				if(a[j].cnt>=K){a[j].ss=pos;break;}
				pos=a[pos].nex;
			}
		}
		ll sss=0;
		for(register int j=1;j<=m;j++) if(v[j]) sss+=1ll*(j-a[j].pre)*(m-a[j].ss+1);
		for(register int j=n;j>=i;j--)
		{
			ans+=sss;
			for(register int k=0;k<ff[j].size();k++)
			{
				int p=ff[j][k];
				int pos=p;
				while(pos!=0)
				{
					if(a[pos].ss<p) break;
					a[pos].cnt--;
					if(a[pos].cnt<K&&a[pos].ss!=m+1)
					{
						sss-=1ll*(pos-a[pos].pre)*(m-a[pos].ss+1);
						int sssss=a[pos].ss;
						a[pos].ss=a[sssss].nex;
						if(a[pos].ss!=m+1) a[pos].cnt+=a[a[pos].ss].sum;
						sss+=1ll*(pos-a[pos].pre)*(m-a[pos].ss+1);
					}
					pos=a[pos].pre;
				}
				a[p].sum--;
				if(!a[p].sum)
				{
					int l=a[p].pre,r=a[p].nex;
					sss-=1ll*(p-l)*(m-a[p].ss+1);
					if(r!=m+1) sss-=1ll*(r-p)*(m-a[r].ss+1);
					a[l].nex=r,a[r].pre=l;
					if(r!=m+1) sss+=1ll*(r-l)*(m-a[r].ss+1);
				}
			}
		}
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2026-01-07 20:32  what_can_I_do  阅读(2)  评论(1)    收藏  举报