题目:
HNOI2016 矿区
SDOI2016 平凡的骰子
JSOI2018 绝地反击
HNOI2004 最佳包裹
SCOI2015 小凸想跑步
APIO2018 选圆圈
椭圆面积异或并
HNOI2016 矿区
首先将平面图转成对偶图。将每个点为端点的边按极角排序,然后按逆时针顺序遍历每个区域,算出面积并编号,求出对偶图。
求出对偶图的生成树,一个原图的块在生成树上仍然是联通块,遍历询问的边时判断询问的联通块和该边在树上的关系即可找到对应子树的贡献。
时间复杂度
O
(
n
log
2
n
+
∑
d
)
O(n \log_2 n+\sum d)
O(nlog2n+∑d),空间复杂度
O
(
n
)
O(n)
O(n)
#include<stdio.h>
#include<math.h>
#include<vector>
#include<algorithm>
#include<unordered_map>
using namespace std;
#define R register int
#define L long long
#define I inline
#define N 200001
#define O 400000
#define M 1200000
I L Gcd(L x,L y){
return y==0?x:Gcd(y,x%y);
}
int PosX[N],PosY[N],f[O],bel[M],rk[M];
bool vis[M];
vector<int>H[O];
L sz2[O],sz1[O],Area[O];
struct Edge{
int Id,End;
};
I Edge Pair(int y,int d){
Edge res;
res.End=y;
res.Id=d;
return res;
}
vector<Edge>G[N];
I int GetF(int x){
if(f[x]==x){
return x;
}
f[x]=GetF(f[x]);
return f[x];
}
I void DFS(int x,int F){
sz2[x]=(L)Area[x]*Area[x];
sz1[x]=Area[x];
for(int T:H[x]){
if(T!=F){
DFS(T,x);
sz1[x]+=sz1[T];
sz2[x]+=sz2[T];
}
}
}
unordered_map<L,int>Q;
I void Calc(int a,int b,L&res1,L&res2){
int eid=Q[(L)a*N+b],x,y;
if(vis[eid]==true){
x=bel[eid];
y=bel[eid^1];
if(sz1[x]>sz1[y]){
res1-=sz2[y];
res2-=sz1[y];
}else{
res1+=sz2[x];
res2+=sz1[x];
}
}
}
int main(){
int n,m,q,x,y,tot=0,root;
scanf("%d%d%d",&n,&m,&q);
for(R i=1;i<=n;i++){
scanf("%d%d",PosX+i,PosY+i);
}
for(R i=0;i!=m;i++){
scanf("%d%d",&x,&y);
G[x].push_back(Pair(y,i<<1));
G[y].push_back(Pair(x,i<<1|1));
Q[(L)x*N+y]=i<<1;
Q[(L)y*N+x]=i<<1|1;
}
for(R i=1;i<=n;i++){
vector<pair<double,pair<int,int>>>E;
for(Edge T:G[i]){
E.push_back(make_pair(atan2(PosY[T.End]-PosY[i],PosX[T.End]-PosX[i]),make_pair(T.End,T.Id)));
}
sort(E.begin(),E.end());
vector<Edge>().swap(G[i]);
int r=0;
for(auto T=E.rbegin();T!=E.rend();T++){
G[i].push_back(Pair(T->second.first,T->second.second));
rk[T->second.second]=r;
r++;
}
}
for(R i=1;i<=n;i++){
for(Edge T:G[i]){
if(vis[T.Id]==false){
L S=0;
int d1=i,d2=T.End,e=T.Id;
do{
vis[e]=true;
bel[e]=tot;
S+=PosX[d1]*PosY[d2]-PosY[d1]*PosX[d2];
d1=d2;
int r=rk[e^1]+1;
if(r==G[d2].size()){
r=0;
}
d2=G[d1][r].End;
e=G[d1][r].Id;
}while(d1!=i);
if(S>0){
Area[tot]=S;
}else{
root=tot;
}
f[tot]=tot;
tot++;
}
}
}
for(R i=0;i!=m;i++){
x=bel[i<<1];
y=bel[i<<1|1];
if(GetF(x)!=GetF(y)){
f[f[x]]=y;
H[x].push_back(y);
H[y].push_back(x);
}else{
vis[i<<1]=vis[i<<1|1]=false;
}
}
L ansN=0,ansD;
Area[root]=1;
DFS(root,-1);
for(R i=0;i!=q;i++){
int d;
scanf("%d",&d);
d=(ansN+d)%n+1;
vector<int>A(d);
for(R j=0;j!=d;j++){
scanf("%d",&A[j]);
A[j]=(ansN+A[j])%n+1;
}
ansN=ansD=0;
Calc(A.back(),A[0],ansN,ansD);
for(R i=1;i!=d;i++){
Calc(A[i-1],A[i],ansN,ansD);
}
ansD<<=1;
L g=Gcd(ansN,ansD);
ansN/=g;
printf("%lld %lld\n",ansN,ansD/g);
}
return 0;
}
平凡的骰子
先要求出骰子的重心。将每个面分成三角形,和原点连成三棱锥,求这些三棱锥各自的重心和体积最后加权平均即可。
统计答案时需要求二面角的大小。二面角用叉乘求出面的法向量求夹角即可。
#include<stdio.h>
#include<math.h>
#include<vector>
using namespace std;
#define R register int
#define D double
#define I inline
#define PIE 3.1415926535897931
struct Vectors{
D Vx,Vy,Vz;
I void Read(){
scanf("%lf%lf%lf",&Vx,&Vy,&Vz);
}
I D Size(){
return sqrt(Vx*Vx+Vy*Vy+Vz*Vz);
}
I auto friend operator+(Vectors A,Vectors B){
A.Vx+=B.Vx;
A.Vy+=B.Vy;
A.Vz+=B.Vz;
return A;
}
I auto friend operator-(Vectors A,Vectors B){
A.Vx-=B.Vx;
A.Vy-=B.Vy;
A.Vz-=B.Vz;
return A;
}
I auto friend operator*(Vectors A,D b){
A.Vx*=b;
A.Vy*=b;
A.Vz*=b;
return A;
}
I D friend operator*(Vectors A,Vectors B){
return A.Vx*B.Vx+A.Vy*B.Vy+A.Vz*B.Vz;
}
I auto friend operator^(Vectors A,Vectors B){
Vectors C;
C.Vx=A.Vy*B.Vz-A.Vz*B.Vy;
C.Vy=A.Vz*B.Vx-A.Vx*B.Vz;
C.Vz=A.Vx*B.Vy-A.Vy*B.Vx;
return C;
}
I D friend operator&(Vectors A,Vectors B){
return acos(A*B/A.Size()/B.Size());
}
}p[50];
I auto Pair(D x,D y,D z){
Vectors res;
res.Vx=x;
res.Vy=y;
res.Vz=z;
return res;
}
vector<int>A[80];
int main(){
int n,m,d,x;
scanf("%d%d",&n,&m);
for(R i=0;i!=n;i++){
p[i].Read();
}
Vectors G=Pair(0,0,0);
D TotV=0;
for(R i=0;i!=m;i++){
scanf("%d",&d);
for(R j=0;j!=d;j++){
scanf("%d",&x);
A[i].push_back(x-1);
}
for(R j=2;j!=d;j++){
Vectors CurG=p[A[i][0]]+p[A[i][j-1]]+p[A[i][j]];
D CurV=p[A[i][0]]*(p[A[i][0]]-p[A[i][j]]^p[A[i][j-1]]-p[A[i][0]]);
G=G+CurG*CurV;
TotV+=CurV;
}
}
G=G*(.25/TotV);
for(R i=0;i!=n;i++){
p[i]=p[i]-G;
}
for(R i=0;i!=m;i++){
D ans=0;
d=A[i].size();
Vectors V0=p[A[i][0]];
for(R j=2;j!=d;j++){
Vectors V1=p[A[i][j-1]],V2=p[A[i][j]];
ans+=((V0^V1)&(V2^V1))+((V1^V2)&(V0^V2))+((V2^V0)&(V1^V0))-PIE;
}
printf("%.7lf\n",.25*ans/PIE);
}
return 0;
}
JSOI2018 绝地反击
根据题意,二分答案。根据答案可以求出每个飞船在圆上可以到达的弧。容易发现,有效的解最多只有
n
n
n 个,只要将这
n
n
n 个弧的端点逐个尝试即可。确定每个飞船最终的位置之后可以建出二分图判断是否存在完美匹配。由于每个飞船连的点时一个区间,直接断环成链然后用霍尔定理即可。
时间复杂度
O
(
n
3
lg
R
)
O(n^3 \lg R)
O(n3lgR),空间复杂度
O
(
n
)
O(n)
O(n)
#include<stdio.h>
#include<math.h>
#include<vector>
#include<algorithm>
using namespace std;
#define R register int
#define D double
#define I inline
#define EPS 1e-8
#define PIE 3.1415926535897931
I int Min(const int x,const int y){
return x<y?x:y;
}
int px[200],py[200];
D theL[200],theR[200],w[200];
bool tag[200];
I D Dis(D Ax,D Ay){
return sqrt(Ax*Ax+Ay*Ay);
}
vector<int>H[400];
I bool CheckRange(D x,D l,D r){
if(l>r){
return x>=l||x<=r;
}
return l<=x&&x<=r;
}
I bool Solve(int n,D the){
D del=PIE*2/n;
int tot=0;
for(R i=0;i!=n<<1;i++){
vector<int>().swap(H[i]);
}
for(R i=0;i!=n;i++){
if(tag[i]==true){
tot++;
}
w[i]=the;
the+=del;
if(the>=PIE){
the-=PIE*2;
}
}
sort(w,w+n);
for(R i=0;i!=n;i++){
if(tag[i]==false){
int lf=lower_bound(w,w+n,theL[i])-w,rt;
if(lf==n){
lf=0;
}
if(CheckRange(w[lf],theL[i],theR[i])==false){
return false;
}
rt=lf;
int p=rt+1;
if(p==n){
p=0;
}
while(p!=lf){
if(CheckRange(w[p],theL[i],theR[i])==false){
break;
}
rt=p;
p++;
if(p==n){
p=0;
}
}
if(lf>rt){
H[rt+n].push_back(lf);
}else{
H[rt].push_back(lf);
H[rt+n].push_back(lf+n);
}
}
}
for(R i=0;i<=n;i++){
int cur=0;
for(R j=i;j!=i+n;j++){
for(int T:H[j]){
if(T>=i){
cur++;
}
}
if(j==i+n-1){
cur+=tot;
}
if(cur>j-i+1){
return false;
}
}
}
return true;
}
I bool Check(int n,int r,D lim){
vector<D>A;
for(R i=0;i!=n;i++){
D l=Dis(px[i],py[i]),cosThe;
cosThe=(l*l+r*r-lim*lim)/(l*r*2);
if(cosThe>1){
return false;
}
if(cosThe>-1){
tag[i]=false;
D the=atan2(px[i],py[i]),del;
del=acos(cosThe);
theL[i]=the-del;
theR[i]=the+del;
if(theL[i]<-PIE){
theL[i]+=PIE*2;
}
if(theR[i]>=PIE){
theR[i]-=PIE*2;
}
A.push_back(theL[i]<-PIE?theL[i]+PIE*2:theL[i]);
}else{
tag[i]=true;
}
}
if(A.empty()==true){
A.push_back(0);
}else{
sort(A.begin(),A.end());
}
D Last=-1e9;
for(D T:A){
if(T-Last>EPS){
if(Solve(n,T)==true){
return true;
}
Last=T;
}
}
return false;
}
int main(){
int n,r;
scanf("%d%d",&n,&r);
for(R i=0;i!=n;i++){
scanf("%d%d",px+i,py+i);
}
D lf=0,rt=300,mid,ans;
while(lf<rt){
mid=.5*(lf+rt);
if(Check(n,r,mid)==true){
ans=mid;
rt=mid-EPS;
}else{
lf=mid+EPS;
}
}
printf("%.9lf",ans);
return 0;
}