BZOJ4541: [Hnoi2016]矿区(最小左转法)

本文介绍了一种解决平面图问题的方法,通过将其转换为对偶图,并利用深度优先搜索(DFS)树来统计特定联通块的属性。文章详细阐述了如何通过遍历对偶图的DFS树来高效地解决涉及平面图割裂的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

题解:
这道题的重边让我调了一下午。。

先将此平面图转化为对偶图。

注意到对偶图的dfs树的任意子树对应原图一个联通块,而此题目相当于给你把树割下若干段求和。 那么每条边暴力判断是往子树还是往父亲从而统计答案。

#include <bits/stdc++.h>
#include <tr1/unordered_map>
typedef long long LL;
using namespace std;
inline LL rd() {
    char ch=getchar(); LL i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
    return i*f;
}

const int N=5e5+50,M=2e6+50;
tr1::unordered_map <int,int> S[N],S2[N];
vector < pair<double,int> > edge[N];
int n,m,q,xc[N],yc[N],g[N],nt[M],vt[M],nxt[M],ww[M],ec=1;
inline void add(int x,int y) {
    if(S[x][y]) return;
    nt[++ec]=g[x]; g[x]=ec; vt[ec]=y;
    S[x][y]=ec;
    edge[x].push_back(make_pair(atan2(yc[y]-yc[x],xc[y]-xc[x]),ec));
}
int vis[M],cnt,rt,stk[M],bl[M],top;
LL s[M],s2[M];
int g2[M],nt2[M],vt2[M],fm[M],ec2=1,istr[M];
inline void add2(int x,int y,int id) {
    if(S2[x][y]) return;
    nt2[++ec2]=g2[x]; g2[x]=ec2; vt2[ec2]=y;
    S2[x][y]=1; ww[id]=ec2; fm[ec2]=x;
}
int dep[M];
inline void dfs(int x) {
    vis[x]=1;
    for(int e=g2[x];e;e=nt2[e]) {
        if(vis[vt2[e]]) continue;
        dep[vt2[e]]=dep[x]+1;
        dfs(vt2[e]); istr[e]=1; istr[e^1]=-1; 
        s[x]+=s[vt2[e]]; s2[x]+=s2[vt2[e]];
    }
}
inline LL cross(int x,int y) {return (LL)xc[x]*yc[y]-(LL)yc[x]*xc[y];}
inline LL gcd(LL a,LL b) {return b?gcd(b,a%b):a;}
int main() {
    n=rd(), m=rd(), q=rd();
    for(int i=1;i<=n;i++) xc[i]=rd(), yc[i]=rd();
    for(int i=1;i<=m;i++) {
        int x=rd() ,y=rd();
        if(x!=y) add(x,y),add(y,x);
    }
    for(int i=1;i<=n;i++) {
        sort(edge[i].begin(), edge[i].end());
        for(int e=1;e<edge[i].size();++e) 
            nxt[edge[i][e].second^1]=edge[i][e-1].second;
        nxt[edge[i][0].second^1]=edge[i][edge[i].size()-1].second;
    }
    for(int i=2;i<=ec;i++) {
        if(bl[i]) continue;
        stk[top=1]=i;
        while(stk[1]!=nxt[stk[top]])
            stk[top+1]=nxt[stk[top]], top++;
        LL c=cross(vt[stk[top]],vt[stk[1]]);
        for(int j=1;j<top;j++) c+=cross(vt[stk[j]],vt[stk[j+1]]);
        ++cnt;
        if(c>0) s[cnt]=c<<1,s2[cnt]=c*c;
        else rt=cnt;
        for(int j=1;j<=top;j++) bl[stk[j]]=cnt;
    }   
    for(int i=2;i<=ec;i++) add2(bl[i],bl[i^1],i);
    dfs(rt);
    LL lst=0;
    for(int i=1;i<=q;i++) {
        int d=(rd()+lst)%n+1;
        for(int j=1;j<=d;j++) stk[j]=(rd()+lst)%n+1;
        stk[d+1]=stk[1];
        LL c=0, c2=0;
        for(int j=1;j<=d;j++) {
            if(!S[stk[j]].count(stk[j+1])) continue;
            int id=S[stk[j]][stk[j+1]];
            id=ww[id];
            if(!istr[id]) continue;
            int x=(istr[id]>0)?vt2[id]:fm[id];
            c+=istr[id]*s[x];
            c2+=istr[id]*s2[x];
        }   
        c2=abs(c2); c=abs(c); LL t=gcd(c,c2);
        c2/=t; c/=t; lst=c2;
        printf("%lld %lld\n",c2,c);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值