[树形dp] Jzoj P3738 理想城市
题解
- 我们将横纵的贡献分开来考虑,想办法让图变成一棵树,这样就会好做很多
- 先考虑横向的贡献,那么我们可以把纵向一段连续的区块给缩成一个点
- 向左右相邻的连一条边权为1的树边,然后构出一棵树后
- 考虑如果求出每一条边的贡献,显然一条边的贡献就是将这棵树分成两棵树,两棵树上两两点对应
- 所以贡献就是size[x]*(n-size[x])
- 对于纵向的话,将横纵坐标交换一下再做就ok了
代码
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #define ll long long 6 using namespace std; 7 const ll mo=1e9,M=1e5+10,N=3123457; 8 ll n,cnt,mnx=mo,mny=mo,head[M],p[M],ans,hs[N],bz[N],size[M]; 9 struct node {ll x,y;}a[M]; 10 struct edge {ll to,from;}e[M]; 11 bool cmp(node a,node b) { return a.x==b.x?a.y>b.y:a.x<b.x; } 12 ll pd(ll x) 13 { 14 ll y=x%N; 15 for (;hs[y]!=0&&hs[y]!=x;y=(y+1)%N); 16 hs[y]=x; return y; 17 } 18 void insert(ll x,ll y) 19 { 20 if (e[head[x]].to==y) return; 21 e[++cnt].to=y,e[cnt].from=head[x],head[x]=cnt; 22 e[++cnt].to=x,e[cnt].from=head[y],head[y]=cnt; 23 } 24 void build() 25 { 26 sort(a+1,a+n+1,cmp); 27 for (ll i=1;i<=n;i++) bz[pd(a[i].x*n+a[i].y)]=i; 28 for (ll i=n;i>=1;i--) 29 { 30 ll x=a[i].x,y=a[i].y; 31 if (p[i]==0) 32 { 33 p[i]=i,size[i]=1; 34 for (ll j=i-1;j>=1;j--) if (a[j].y==a[j+1].y+1) p[j]=i,size[i]++; else break; 35 } 36 ll q=pd((x+1)*n+y); if (bz[q]) insert(p[i],p[bz[q]]); 37 } 38 } 39 void dfs(ll x,ll fa) 40 { 41 for (ll i=head[x];i;i=e[i].from) if (e[i].to!=fa) dfs(e[i].to,x),size[x]+=size[e[i].to]; 42 for (ll i=head[x];i;i=e[i].from) if (e[i].to!=fa) ans=(ans+size[e[i].to]*(n-size[e[i].to])%mo)%mo; 43 } 44 int main() 45 { 46 freopen("city.in","r",stdin),freopen("city.out","w",stdout),scanf("%lld",&n); 47 for (ll i=1;i<=n;i++) scanf("%lld%lld",&a[i].x,&a[i].y),mnx=min(mnx,a[i].x),mny=min(mny,a[i].y); 48 for (ll i=1;i<=n;i++) a[i].x-=mnx-1,a[i].y-=mny-1; 49 build(),dfs(p[1],0); 50 for (ll i=1;i<=n;i++) swap(a[i].x,a[i].y); 51 cnt=0; for (ll i=1;i<M;i++) head[i]=size[i]=p[i]=0; 52 for (ll i=1;i<N;i++) hs[i]=bz[i]=0; 53 build(),dfs(p[1],0),printf("%lld",ans); 54 }