[2018.10.20 T2] 面包

暂无链接

面包

【问题描述】

小 X 有一块 N ∗ M N*M NM的巨大面包,上面有 K K K个葡萄干,第 i i i个葡萄干的位置在第 A i A_i Ai B i B_i Bi列,任意两个葡萄干都不在同一位置。
现在小 X 的朋友小 C 来了,小 X 要从这块面包上切下呈矩形的一块给小 C。但是如果切下来的面包上一个葡萄干都没有,小 X 会很没面子,于是小 X 的一个合法的切割方案要满足切下的部分是含有至少一个葡萄干的矩形。现在小 X 想知道:所有合法的切割方案切下的部分包含葡萄干的个数的方差是多少?为了方便,请输出答案对 998 , 244 , 353 998,244,353 998,244,353取模的结果,保证数据是在一定范围内随机生成的。

【输入格式】

第一行,三个正整数 N , M N,M NM K K K
接下来 K K K行,每行两个整数 A i A_i Ai B i B_i Bi

【输出格式】

一行,包含一个非负整数,表示答案对 998 , 244 , 353 998,244,353 998,244,353取模的结果。
注意,你的答案应该在 [ 0 , 998 , 244 , 353 ) [0,998,244,353) [0,998,244,353)的范围内。

【输入输出样例 1】
bread. in

1 2 2
1 1
1 2

bread.out

887328314

【输入输出样例说明 1】

有三种合法方案,其中两种方案包含一个葡萄干,一种方案包含两个葡萄干,所以方差为 9 2 \frac{9}{2} 29,对 998244353 998244353 998244353取模得到 887328314 887328314 887328314

【输入输出样例 2】

见下发文件中的 b r e a d 2. i n / b r e a d 2. o u t bread2.in/bread2.out bread2.in/bread2.out

【输入输出样例 3】

见下发文件中的 b r e a d 3. i n / b r e a d 3. o u t bread3.in/bread3.out bread3.in/bread3.out

【数据范围与约定】

对于所有的数据, 1 ≤ N , M ≤ 1 0 9 1≤N,M≤10^9 1N,M109 1 ≤ K ≤ m i n ( N ∗ M , 2000 ) 1≤K≤min(N*M,2000) 1Kmin(NM,2000) 1 ≤ A i ≤ N 1≤A_i≤N 1AiN 1 ≤ B i ≤ M 1≤B_i≤M 1BiM
对于每个子任务的特殊限制:
Subtask1(20pts): N , M ≤ 10 N,M≤10 N,M10
Subtask2(25pts): N , M ≤ 100 N,M≤100 N,M100
Subtask3(15pts): K ≤ 3 K≤3 K3
Subtask4(40pts): 无特殊限制。

题解

按照我不知道的一个套路,我们先化简一波方差的式子,设 x i x_i xi为第 i i i个合法矩形中的点数, n n n为合法的矩形个数:
∑ ( x i − x ‾ ) 2 n = ∑ ( x i 2 − 2 x i x ‾ + x ‾ 2 ) n = ∑ x i 2 n − 2 ∑ x i x ‾ n + ∑ x ‾ 2 n = ∑ x i 2 n − 2 n x ‾ 2 n + n x ‾ 2 n = ∑ x i 2 n − x ‾ 2 \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} ====n(xix)2n(xi22xix+x2)nxi2n2xix+nx2nxi2n2nx2+nnx2nxi2x2

第二个东西非常好求,我们直接枚举每个点,看它对几个矩形有贡献,就求到了 ∑ x i \sum x_i xi;第一个东西又要用到一个套路, x i 2 x_i^2 xi2相当于一个矩形里的点对数(包括自己跟自己),我们只需要 O ( k 2 ) O(k^2) O(k2)枚举点对,看它们对几个矩形产生了贡献,就能统计出 ∑ x i 2 \sum x_i^2 xi2

那么问题来了, n n n怎么求?

我们可以先枚举右下角的行数,在那一行上从左到右加入在右下角上面的点,维护一个左上角坐标范围的单调栈,就能统计出有多少个矩形包含了至少一个点,大致如下图:
1.png
单调栈维护之后:
2.png

愉快地统计出 n n n之后当然要愉快的 A C AC AC啦。

代码
#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();}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值