[bzoj5187][Usaco2018 Jan][数论]Sprinklers

63 篇文章 0 订阅
15 篇文章 0 订阅

Description

农夫约翰有一块很大的田,他正在考虑种甜玉米。经过对他农田的调查,FJ发现它形成了一个(N-1)×(N-1)的

正方形。西南角为坐标(0,0),东北角是(N-1,N-1)。在某些整数坐标的位置中有双头喷头,每一个都能够同
时喷洒水和肥料。一个在(i,j)处的双头喷头会将水洒在农田中所有在其东面且在其北面的区域,将肥料洒在农
田中所有在其南面且在其西面的区域。形式化地说,这个喷头将会将水洒在所有满足N≥x≥i和N≥y≥j的实数坐标
(x,y)上,会将肥料洒在所有满足0≤x≤i和0≤y≤j的实数坐标上农民约翰想在一些边与坐标轴平行的长方形农田
里种植甜玉米。然而,为了甜玉米的生长,矩形内的所有点都必须由双头喷头浇水和施肥。当然,矩形必须有正的
面积,否则农民约翰就不能在里面种植任何玉米!帮助农民约翰确定可以种植甜玉米的拥有正面积的矩形农田数。
由于这个数字可能很大,所以输出对为10^9 + 7取模

Input

第一行包含一个整数N,表示农场的大小(1≤N≤10^5)。

接下来的n行各包含两个由空格分隔的整数。这些整数中0≤i,j≤N-1,这表示有一个双头喷头位于(i,j)的位置。
保证每列都有一台喷头,每排正好有一台喷头。这也就是说,没有两个喷头有相同的x坐标或y坐标。

Output

输出包含一行,表示拥有正面积的合法矩形农田的个数,对10^9+7取模.

Sample Input

5

0 4

1 1

2 2

3 0

4 3

Sample Output

21

题解

比赛的最后半小时想到50分做法然而并没有细想就这么放过了式子题..
预处理能种小麦的点,发现总是组成一个多边形
这个多边形一定是阶梯状的…..画个图就清楚了就一些矩阵面积并
我们把答案的式子写出来

i=0n1j=lw[i]up[i]k=lw[i]j1ilk ∑ i = 0 n − 1 ∑ j = l w [ i ] u p [ i ] ∑ k = l w [ i ] j − 1 i − l k

其中up[i]表示第i列向上能到达的最高行,lw[i]表示向下能到达的最低行, lk l k 表示第k行向左最多能去到哪一列
画图最清晰了
一通乱化
i=0n1j=lw[i]up[i]i(jlw[i])k=lw[i]j1lk ∑ i = 0 n − 1 ∑ j = l w [ i ] u p [ i ] i ∗ ( j − l w [ i ] ) − ∑ k = l w [ i ] j − 1 l k

i=0n1i(1+up[i]lw[i])(up[i]+lw[i])2j=lw[i]+1up[i]k=lw[i]j1lk ∑ i = 0 n − 1 i ∗ ( 1 + u p [ i ] − l w [ i ] ) ∗ ( u p [ i ] + l w [ i ] ) 2 − ∑ j = l w [ i ] + 1 u p [ i ] ∑ k = l w [ i ] j − 1 l k

化到这个地方前面的式子已经可以 O(1) O ( 1 ) 计算了,下来主要化后面的式子
设S[i]表示l数组的前缀和,pre[i]表示S数组的前缀和

j=lw[i]+1up[i]Sj1Slw[i]1 ∑ j = l w [ i ] + 1 u p [ i ] S j − 1 − S l w [ i ] − 1

preup[i]1prelw[i]1Slw[i]1(up[i]lw[i]) p r e u p [ i ] − 1 − p r e l w [ i ] − 1 − S l w [ i ] − 1 ∗ ( u p [ i ] − l w [ i ] )

组合起来即为
i=0n1i(1+up[i]lw[i])(up[i]+lw[i])2(preup[i]1prelw[i]1Slw[i]1(up[i]lw[i])) ∑ i = 0 n − 1 i ∗ ( 1 + u p [ i ] − l w [ i ] ) ∗ ( u p [ i ] + l w [ i ] ) 2 − ( p r e u p [ i ] − 1 − p r e l w [ i ] − 1 − S l w [ i ] − 1 ∗ ( u p [ i ] − l w [ i ] ) )

于是 O(n) O ( n ) 扫一遍就出答案了..

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define LL long long
using namespace std;
inline int read()
{
    int f=1,x=0;char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct matrix{int x,y;}a[1000005];
bool cmp(matrix n1,matrix n2){return n1.x<n2.x;}
const LL mod=1e9+7;
LL pow_mod(LL a,int b)
{
    LL ret=1;
    while(b)
    {
        if(b&1)ret=ret*a%mod;
        a=a*a%mod;b>>=1;
    }
    return ret;
}
int n;
LL lw[1000005],up[1000005],L[1000005];
LL S[1000005],pre[1000005];
int main()
{
    n=read();
    for(int i=1;i<=n;i++)a[i].x=read(),a[i].y=read();
    sort(a+1,a+1+n,cmp);
    memset(up,-1,sizeof(up));
    memset(lw,-1,sizeof(lw));
    memset(L,-1,sizeof(L));
    up[a[n].x]=a[n].y;int mx=a[n].y;
    for(int i=n-1;i>=1;i--)
        if(mx<a[i].y)
        {
            up[a[i].x+1]=mx;up[a[i].x]=a[i].y;
            mx=a[i].y;
        }
    for(int i=n-1;i>=0;i--)if(up[i]==-1)up[i]=up[i+1];
    lw[a[1].x]=a[1].y;int mn=a[1].y;
    L[a[1].y]=a[1].x;int last=a[1].x;
    for(int i=2;i<=n;i++)
        if(mn>a[i].y)
        {
            L[a[i].y]=a[i].x;
            lw[a[i].x-1]=mn;lw[a[i].x]=a[i].y;
            mn=a[i].y;last=a[i].x;
        }
    for(int i=0;i<n;i++)if(lw[i]==-1)lw[i]=lw[i-1];
    for(int i=0;i<n;i++)if(L[i]==-1)L[i]=L[i-1];
//  printf("%d\n",L[546]);
/*  for(int i=0;i<n;i++)printf("%d ",up[i]);
    puts("");
    for(int i=0;i<n;i++)printf("%d ",lw[i]);
    puts("");
    for(int i=0;i<n;i++)printf("%d ",L[i]);
    puts("");*/

    //init
    for(int i=0;i<n;i++)S[i]=(S[i-1]+L[i])%mod;
    for(int i=0;i<n;i++)pre[i]=(pre[i-1]+S[i])%mod;
    LL ans=0;
    for(int i=0;i<n;i++)
    {
        LL cnt=(LL)((i*(up[i]-lw[i]+1))*(up[i]-lw[i])/2)%mod;
        LL tmp=pre[up[i]-1]-pre[lw[i]-1]-S[lw[i]-1]*(up[i]-lw[i])%mod;
        while(tmp<0)tmp+=mod;
        ans=(ans+cnt-tmp);
        while(ans<0)ans+=mod;
        ans%=mod;
    }
    printf("%lld\n",ans);
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值