矿区
平面上的矿区划分成了若干个开发区域。
简单地说,你可以将矿区看成一张连通的平面图,平面图划分为了若干平面块,每个平面块即为一个开发区域,平面块之间的边界必定由若干整点(坐标值为整数的点)和连接这些整点的线段组成。每个开发区域的矿量与该开发区域的面积有关:具体而言,面积为(s)的开发区域的矿量为(s^2)。
现在有(m)个开采计划。每个开采计划都指定了一个由若干开发区域组成的多边形,一个开采计划的优先度被规定为矿量的总和÷开发区域的面积和;例如,若某开采计划指定两个开发区域,面积分别为(a)和(b),则优先度为((a^2+b^2)/(a+b))。由于平面图是按照划分开发区域边界的点和边给出的,因此每个开采计划也只说明了其指定多边形的边界,并未详细指明是哪些开发区域(但很明显,只要给出了多边形的边界就可以求出是些开发区域)。
你的任务是求出每个开采计划的优先度。为了避免精度问题,你的答案必须按照分数的格式输出,即求出分子和分母,且必须是最简形式(分子和分母都为整数,而且都消除了最大公约数;
由于某些原因,你必须依次对每个开采计划求解(即下一个开采计划会按一定格式加密,加密的方式与上一个开采计划的答案有关)。具体的加密方式见输入格式。
对于 $ 100 % $ 的数据,$ n, k leq 2 imes 10^5, m leq 3n-6, |x_i|, |y_i| leq 3×10^4$。所有开采计划的 $ d $ 之和不超过 (2 imes 10^6)。保证任何开采计划都包含至少一个开发区域,且这些开发区域构成一个连通块。保证所有开发区域的矿量和不超过 $ 2^{63}-1 $。保证平面图中没有多余的点和边。保证数据合法。由于输入数据量较大,建议使用读入优化。
题解
https://www.cnblogs.com/heyujun/p/10447472.html
先平面图转对偶图,
建好了对偶图之后随意拿出一个生成树,以无边界的范围为根。
无边界的范围很好求,用叉积算出有向面积时,算出来是负数的就是无边界的范围。
然后标记所有的树边,记录生成树中每个子树的矿区面积和及面积平方和。
对于每一个询问,先找到询问里出现的边,如果有非树边就忽略,否则如果这条边所在的面是儿子,就加上子树的面积,如果是父亲就减去儿子子树的面积。
这个可以通过画图手玩进行证明。
struct point {int64 x,y;};
IN point operator-(CO point&a,CO point&b){
return {a.x-b.x,a.y-b.y};
}
IN int64 cross(CO point&a,CO point&b){
return a.x*b.y-a.y*b.x;
}
IN float128 angle(CO point&a,CO point&b){
return atan2((float128)b.y-a.y,b.x-a.x);
}
struct edge {int u,v;};
CO int N=2e6+10;
point p[N];
edge e[N];
map<pair<int,int>,int> loc;
float128 ang[N];
vector<int> to[N];
int nxt[N],adj[N];
array<int64,2> sum[N];
IN array<int64,2> operator+(CO array<int64,2>&a,CO array<int64,2>&b){
return {a[0]+b[0],a[1]+b[1]};
}
IN array<int64,2> operator-(CO array<int64,2>&a,CO array<int64,2>&b){
return {a[0]-b[0],a[1]-b[1]};
}
edge e2[N];
vector<int> to2[N];
int fa[N],vis[N],tree[N];
void dfs(int u){
vis[u]=1;
for(int i:to2[u]){
int v=e2[i].v;
if(vis[v]) continue;
tree[i]=tree[i^1]=1;
// cerr<<"tree "<<u<<" "<<v<<endl;
fa[v]=u,dfs(v);
sum[u]=sum[u]+sum[v];
}
}
int main(){
int n=read<int>(),m=read<int>(),Q=read<int>();
for(int i=1;i<=n;++i) read(p[i].x),read(p[i].y);
for(int i=1;i<=m;++i){
int u=read<int>(),v=read<int>();
e[i<<1]={u,v},ang[i<<1]=angle(p[u],p[v]),to[u].push_back(i<<1);
e[i<<1|1]={v,u},ang[i<<1|1]=angle(p[v],p[u]),to[v].push_back(i<<1|1);
loc[{u,v}]=i<<1,loc[{v,u}]=i<<1|1;
}
for(int i=1;i<=n;++i){
sort(to[i].begin(),to[i].end(),[&](int a,int b)->bool{
return ang[a]<ang[b];
});
for(int j=0;j<(int)to[i].size();++j)
nxt[to[i][j]^1]=to[i][(j+to[i].size()-1)%to[i].size()];
}
int idx=0,root=-1;
for(int i=2;i<=2*m+1;++i)if(!adj[i]){
adj[i]=++idx;
// cerr<<idx<<" face="<<endl;
// cerr<<" ("<<p[e[i].u].x<<","<<p[e[i].u].y<<") -> ("<<p[e[i].v].x<<","<<p[e[i].v].y<<")"<<endl;
int64 area=0;
point st=p[e[i].u];
for(int j=nxt[i];j!=i;j=nxt[j]){
adj[j]=idx;
// cerr<<" ("<<p[e[j].u].x<<","<<p[e[j].u].y<<") -> ("<<p[e[j].v].x<<","<<p[e[j].v].y<<")"<<endl;
area+=cross(p[e[j].u]-st,p[e[j].v]-st);
}
if(area<0) root=idx;
else sum[idx]={area*area,2*area}; // 4 times
}
for(int i=2;i<=2*m+1;++i){
e2[i]={adj[i],adj[i^1]};
to2[adj[i]].push_back(i);
}
dfs(root);
array<int64,2> ans={};
while(Q--){
static int q[N];
int c=(read<int64>()+ans[0])%n+1;
for(int i=1;i<=c;++i) q[i]=(read<int64>()+ans[0])%n+1;
q[c+1]=q[1];
ans={0,0};
for(int i=1;i<=c;++i){
int id=loc[{q[i],q[i+1]}];
if(!tree[id]) continue;
int u=adj[id],v=adj[id^1];
// cerr<<"side "<<u<<" "<<v<<endl;
if(fa[u]==v) ans=ans+sum[u];
else if(fa[v]==u) ans=ans-sum[v];
}
int64 g=gcd(ans[0],ans[1]);
ans[0]/=g,ans[1]/=g;
printf("%lld %lld
",ans[0],ans[1]);
}
return 0;
}