您的位置:首页 > 其它

bzoj4558 [JLoi2016]方

2017-01-01 10:25 309 查看
【题意】

n*m网格,k个坏点,求四个顶点均不是坏点的正方形个数,对(10^8+7)取模。正方形可以斜着放。

【数据范围】

n<=10^6,m<=10^6,k<=2000

【题解】

正着不好做,考虑容斥:

答案=总数-至少1个顶点是坏点+至少2个顶点是坏点-至少3个顶点是坏点+4个顶点都是坏点

1.计算总数:

对于任意一个斜着的正方形,我们画出它外围的正着的正方形框子。

对于每个框子,它包含的正方形个数可以计算。

故枚举框子边长进行计算。

时间复杂度O(n)。

2.计算至少1个顶点是坏点:

类似于1,枚举坏点,对于一个包含它的框子,其中只有一个正方形满足一个顶点是它。

故计算包含它的框子个数即可,发现是多个等差数列求和。

时间复杂度O(k)。

3.计算至少2个顶点是坏点,至少3个顶点是坏点,4个顶点都是坏点。

将坏点hash。

枚举两个坏点并分类讨论:

两个坏点是一条边/对角线,计算其余两点的坐标,统计答案。

时间复杂度O(k^2)。

【时间复杂度】

O(n+k^2)

#include<cstdio>
#include<cstring>
#include<algorithm>
#define mod 100000007
#define ll long long
#define N 1000010
#define P 100003
using namespace std;

struct aa{int x, y;}a[2010];
struct bb{ll x; int next;}b[2010];
int n, m, k, ans0, ans1, ans2, ans3, ans4, ans, s
, x1, y1, x2, y2, p[100010], x, l;
ll xx;

int check(int l1, int l2, int h){
if(l1>l2)swap(l1, l2); int sum=0;
if(h<=l1-1)return (s[h]-1+mod)%mod;
sum=(s[l1-1]-1+mod)%mod;
if(h<=l2)return (sum+(ll)(h-l1+1)*l1%mod)%mod;
sum=(sum+(ll)(l2-l1+1)*l1%mod)%mod;
if(h<=l1+l2-1)return (sum+s[l1-1]-s[l1+l2-1-h]+mod)%mod;
return (sum+s[l1-1])%mod;
}

int pow(int y, int x){
int s=1, t=y;
while(x){
if(x&1)s=(ll)s*t%mod;
t=(ll)t*t%mod; x>>=1;
}
return s;
}

int exist(int x1, int y1){
ll xx=(ll)x1*1000000000+y1; int x=xx%P;
for(int i=p[x]; i; i=b[i].next)
if(b[i].x==xx)return 1;
return 0;
}

int main(){
scanf("%d%d%d", &n, &m, &k); n++; m++;
for(int i=1; i<=k; i++){
scanf("%d%d", &a[i].x, &a[i].y);
a[i].x++; a[i].y++;
}
ans0=0;
for(int i=2; i<=min(n, m); i++)ans0=(ans0+(ll)(i-1)*(n-i+1)%mod
4000
*(m-i+1)%mod)%mod;
ans1=0;
s[0]=0; for(int i=1; i<=1000005; i++)s[i]=(s[i-1]+i)%mod;
for(int i=1; i<=k; i++){
ans1=(ans1+check(a[i].x, n-a[i].x+1, a[i].y))%mod;
ans1=(ans1+check(a[i].x, n-a[i].x+1, m-a[i].y+1))%mod;
ans1=(ans1+check(a[i].y, m-a[i].y+1, a[i].x))%mod;
ans1=(ans1+check(a[i].y, m-a[i].y+1, n-a[i].x+1))%mod;
ans1=(ans1-min(a[i].x-1, a[i].y-1)+mod)%mod;
ans1=(ans1-min(a[i].x-1, m-a[i].y)+mod)%mod;
ans1=(ans1-min(n-a[i].x, a[i].y-1)+mod)%mod;
ans1=(ans1-min(n-a[i].x, m-a[i].y)+mod)%mod;
}

l=0; memset(p, 0, sizeof(p));
for(int i=1; i<=k; i++){
xx=(ll)a[i].x*1000000000+a[i].y; x=xx%P;
b[++l].x=xx; b[l].next=p[x]; p[x]=l;
}

ans2=ans3=ans4=0;
for(int i=1; i<=k; i++)
for(int j=1; j<=k; j++)if(i!=j){
x1=a[i].x+a[i].y-a[j].y; y1=a[i].y+a[j].x-a[i].x;
x2=a[j].x+a[i].y-a[j].y; y2=a[j].y+a[j].x-a[i].x;
if(x1>=1&&x1<=n&&y1>=1&&y1<=m&&x2>=1&&x2<=n&&y2>=1&&y2<=m){
ans2++;
if(exist(x1, y1))ans3++; if(exist(x2, y2))ans3++;
if(exist(x1, y1)&&exist(x2, y2))ans4++;
}
x1=a[i].x+a[j].y-a[i].y; y1=a[i].y+a[i].x-a[j].x;
x2=a[j].x+a[j].y-a[i].y; y2=a[j].y+a[i].x-a[j].x;
if(x1>=1&&x1<=n&&y1>=1&&y1<=m&&x2>=1&&x2<=n&&y2>=1&&y2<=m){
ans2++;
if(exist(x1, y1))ans3++; if(exist(x2, y2))ans3++;
if(exist(x1, y1)&&exist(x2, y2))ans4++;
}
if((a[i].x-a[j].x+a[i].y-a[j].y)%2==0){
x1=a[i].x+(a[i].y-a[j].y+a[j].x-a[i].x)/2; y1=a[i].y+(a[j].x-a[i].x+a[j].y-a[i].y)/2;
x2=a[j].x-(a[i].y-a[j].y+a[j].x-a[i].x)/2; y2=a[j].y-(a[j].x-a[i].x+a[j].y-a[i].y)/2;
if(x1>=1&&x1<=n&&y1>=1&&y1<=m&&x2>=1&&x2<=n&&y2>=1&&y2<=m)ans2++;
}
}
ans2=(ll)ans2*pow(2, mod-2)%mod;
ans3=(ll)ans3*pow(4, mod-2)%mod;
ans4=(ll)ans4*pow(8, mod-2)%mod;
ans=(ans0-ans1+ans2-ans3+ans4)%mod; if(ans<0)ans+=mod; printf("%d", ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: