[2018.10.20 T2] 麪包

暫無鏈接

麪包

【問題描述】

小 X 有一塊NMN*M的巨大面包,上面有KK個葡萄乾,第ii個葡萄乾的位置在第AiA_iBiB_i列,任意兩個葡萄乾都不在同一位置。
現在小 X 的朋友小 C 來了,小 X 要從這塊麪包上切下呈矩形的一塊給小 C。但是如果切下來的麪包上一個葡萄乾都沒有,小 X 會很沒面子,於是小 X 的一個合法的切割方案要滿足切下的部分是含有至少一個葡萄乾的矩形。現在小 X 想知道:所有合法的切割方案切下的部分包含葡萄乾的個數的方差是多少?爲了方便,請輸出答案對998,244,353998,244,353取模的結果,保證數據是在一定範圍內隨機生成的。

【輸入格式】

第一行,三個正整數NMN,MKK
接下來KK行,每行兩個整數AiA_iBiB_i

【輸出格式】

一行,包含一個非負整數,表示答案對998,244,353998,244,353取模的結果。
注意,你的答案應該在[0,998,244,353)[0,998,244,353)的範圍內。

【輸入輸出樣例 1】
bread. in

1 2 2
1 1
1 2

bread.out

887328314

【輸入輸出樣例說明 1】

有三種合法方案,其中兩種方案包含一個葡萄乾,一種方案包含兩個葡萄乾,所以方差爲92\frac{9}{2},對998244353998244353取模得到887328314887328314

【輸入輸出樣例 2】

見下發文件中的bread2.in/bread2.outbread2.in/bread2.out

【輸入輸出樣例 3】

見下發文件中的bread3.in/bread3.outbread3.in/bread3.out

【數據範圍與約定】

對於所有的數據,1N,M1091≤N,M≤10^91Kmin(NM,2000)1≤K≤min(N*M,2000)1AiN1≤A_i≤N1BiM1≤B_i≤M
對於每個子任務的特殊限制:
Subtask1(20pts): N,M10N,M≤10
Subtask2(25pts): N,M100N,M≤100
Subtask3(15pts): K3K≤3
Subtask4(40pts): 無特殊限制。

題解

按照我不知道的一個套路,我們先化簡一波方差的式子,設xix_i爲第ii個合法矩形中的點數,nn爲合法的矩形個數:
(xix)2n=(xi22xix+x2)n=xi2n2xixn+x2n=xi2n2nx2n+nx2n=xi2nx2 \begin{aligned} &\frac{\sum(x_i-\overline x)^2}{n}\\ =&\frac{\sum(x_i^2-2x_i\overline x+\overline x^2)}{n}\\ =&\frac{\sum x_i^2}{n}-\frac{2\sum x_i\overline x}{n}+\frac{\sum\overline x^2}{n}\\ =&\frac{\sum x_i^2}{n}-\frac{2n\overline x^2}{n}+\frac{n\overline x^2}{n}\\ =&\frac{\sum x_i^2}{n}-\overline x^2\\ \end{aligned}

第二個東西非常好求,我們直接枚舉每個點,看它對幾個矩形有貢獻,就求到了xi\sum x_i;第一個東西又要用到一個套路,xi2x_i^2相當於一個矩形裏的點對數(包括自己跟自己),我們只需要O(k2)O(k^2)枚舉點對,看它們對幾個矩形產生了貢獻,就能統計出xi2\sum x_i^2

那麼問題來了,nn怎麼求?

我們可以先枚舉右下角的行數,在那一行上從左到右加入在右下角上面的點,維護一個左上角座標範圍的單調棧,就能統計出有多少個矩形包含了至少一個點,大致如下圖:
1.png
單調棧維護之後:
2.png

愉快地統計出nn之後當然要愉快的ACAC啦。

代碼
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=2005,mod=998244353;
struct sd{int x,y;}ptx[M],pty[M],sta[M];
int sum[M],tot,top,sum1,sum2,n,m,k;
bool cmpy(sd a,sd b){return a.y==b.y?a.x>b.x:a.y<b.y;}
bool cmpx(sd a,sd b){return a.x<b.x;}
ll power(ll x,ll p){ll r=1;for(;p;p>>=1,x=x*x%mod)if(p&1)r=r*x%mod;return r;}
void in(){scanf("%d%d%d",&n,&m,&k);for(int i=1;i<=k;++i)scanf("%d%d",&ptx[i].x,&ptx[i].y),pty[i]=ptx[i];}
void ac()
{
	for(int i=1;i<=k;++i)(sum1+=1ll*ptx[i].x*(n-ptx[i].x+1)%mod*ptx[i].y%mod*(m-ptx[i].y+1)%mod)%=mod;
	for(int i=1;i<=k;++i)for(int j=1;j<=k;++j)
	(sum2+=1ll*min(ptx[i].x,ptx[j].x)*(n-max(ptx[i].x,ptx[j].x)+1)%mod*min(ptx[i].y,ptx[j].y)%mod*(m-max(ptx[i].y,ptx[j].y)+1)%mod)%=mod;
	sort(ptx+1,ptx+1+k,cmpx);sort(pty+1,pty+1+k,cmpy);ptx[k+1].x=n+1;
	for(int i=k,j,tmp;i>=1;--i)if(ptx[i].x!=ptx[i+1].x)
	{
		for(top=0,tmp=0,j=1;j<=k;++j)
		{
			if(pty[j].x>ptx[i].x)continue;
			(tmp+=1ll*sum[top]*(pty[j].y-sta[top].y)%mod)%=mod;
			for(;pty[j].x>sta[top].x&--top);
			sta[++top]=pty[j];
			sum[top]=(sum[top-1]+1ll*pty[j].x*(pty[j].y-sta[top-1].y)%mod)%mod;
		}
		(tot+=1ll*(tmp+1ll*sum[top]*(m-sta[top].y+1)%mod)*(ptx[i+1].x-ptx[i].x)%mod)%=mod;
	}
	tot=power(tot,mod-2);
	printf("%lld",(1ll*sum2*tot%mod-1ll*sum1*tot%mod*sum1%mod*tot%mod+mod)%mod);
}
int main(){in(),ac();}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章