【JZOJ3738】理想城市(city)
Description
Solution
把求的答案变一下,相当于一个非空的格可以往下走,答案就要加上经过这一步的路径总数。
那么,我们把横着连在一起的非空格缩成一个点(连通块),如果两个点上下有接触,就给两个点连边。这样构建的图一定是一棵树,那么我们考虑一条树边的贡献,就是它的两个端点分别连出去的点包含着非空格子数相乘。
竖着的同理。
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define rep(i,x) for(int i=ls[x];i;i=nx[i])
#define N 100010
#define M 200010
#define mod 3000007
#define ll long long
#define inf 2147483647
#define mo 1000000000
using namespace std;
struct node{
int x,y;
}b[N];
int to[M],nx[M],ls[N],num=0;
void link(int x,int y){
to[++num]=y,nx[num]=ls[x],ls[x]=num;
}
ll h[mod];
int r=0,c=0;
int o[mod];
int g(int x,int y){
return (ll)(x-1)*c+y;
}
int f[N],fc[N],sz[N];
int n;
ll ans=0;
int getf(int x){
return f[x]==x?x:f[x]=getf(f[x]);
}
int getfc(int x){
return fc[x]==x?x:fc[x]=getfc(fc[x]);
}
int hash(ll x)
{
int p=x%mod;
while(h[p] && h[p]!=x) p=(p+1)%mod;
return p;
}
void calc(int x,int t)
{
rep(i,x)
{
int v=to[i];
if(v==t) continue;
calc(v,x);
sz[x]+=sz[v];
}
rep(i,x)
{
int v=to[i];
if(v==t) continue;
ans=(ans+(ll)sz[v]*(n-sz[v]))%mo;
}
}
void work(int v1,int v2)
{
memset(ls,0,sizeof(ls));
num=0;
fo(i,1,n) f[i]=fc[i]=i,sz[i]=1;
fo(i,1,n)
{
int x=b[i].x,y=b[i].y;
if(x+v1>r || y+v2>c) continue;
int w1=hash(g(x,y)),w2=hash(g(x+v1,y+v2));
if(h[w2])
{
int fx=getf(o[w1]),fy=getf(o[w2]);
f[fy]=fx,sz[fx]+=sz[fy];
sz[fy]=0;
}
}
fo(i,1,n)
{
int x=b[i].x,y=b[i].y;
if(x+v2>r || y+v1>c) continue;
int w1=hash(g(x,y)),w2=hash(g(x+v2,y+v1));
if(h[w2])
{
int fx=getf(o[w1]),fy=getf(o[w2]);
int gx=getfc(fx),gy=getfc(fy);
if(gx!=gy) fc[gy]=gx,link(fx,fy),link(fy,fx);
}
}
calc(getf(1),0);
}
int main()
{
freopen("city.in","r",stdin);
freopen("city.out","w",stdout);
scanf("%d",&n);
int p=inf,q=inf;
fo(i,1,n)
{
scanf("%d %d",&b[i].x,&b[i].y);
p=min(p,b[i].x),q=min(q,b[i].y);
r=max(r,b[i].x),c=max(c,b[i].y);
}
p--,q--;
r-=p,c-=q;
fo(i,1,n)
{
b[i].x-=p,b[i].y-=q;
ll t=g(b[i].x,b[i].y);
int wz=hash(t);
o[wz]=i,h[wz]=t;
}
work(0,1);
work(1,0);
printf("%lld",ans);
}