The 2023 ICPC Asia EC Regionals Online Contest (I)G题J题K题

题目链接

G. Spanning Tree

容易知道对于一颗树来说,若将其分成若干块,每两块的链接一定是其中一个块的根节点(深度最小的那个点)连向另一个块中的一个点,反之,若不是这样那么生成的树一定不是同一根树。

所以我们只需要记录给定生成树的点的每个点的父节点,最后再用并查集即可,让深度小的当父亲节点。不用lca,用倍增lca的话会超时。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int mod=998244353;
//#define int long long
int fa[N][20],dep[N],faa[N],siz[N];
vector<int> v[N];
int n;
pair<int,int> p[N];
long long qpow(int a,int b){
	long long res=1;
	while(b){
		if(b&1) res=((long long)res*a)%mod;
		b>>=1;
		a=((long long)a*a)%mod;
	}
	return res;
}
void dfs(int x,int f){
	dep[x]=dep[f]+1;
	fa[x][0]=f;
/*	for(int i=1;i<=19;i++){
		fa[x][i]=fa[fa[x][i-1]][i-1];
	}*/
	for(int i=0;i<v[x].size();i++){
		int j=v[x][i];
		if(j==f) continue;
		dfs(j,x);
	}
}
int lca(int a,int b){
	if(dep[a]<dep[b])
		swap(a,b);
	for(int i=19;i>=0;i--){
		if(dep[fa[a][i]]>=dep[b])
			a=fa[a][i];
	}
	if(a==b) return a;
	for(int i=19;i>=0;i--){
		if(fa[a][i]!=fa[b][i]){
			a=fa[a][0],b=fa[b][0];
		}
	}
	return fa[a][0];
}
int find(int x){
	if(faa[x]==x)
		return x;
	return faa[x]=find(faa[x]);
}
void u(int a,int b){
	a=find(a),b=find(b);
	if(a!=b){
		if(dep[b]<dep[a])
		siz[b]+=siz[a],
		faa[a]=b;
		else
		siz[a]+=siz[b],
		faa[b]=a;
	}
}
void solve(){
	cin>>n;
	for(int i=1;i<=n;i++)
		faa[i]=i,siz[i]=1;
	for(int i=1;i<n;i++){
		int a,b;cin>>a>>b;
		p[i]={a,b};
	}
	for(int i=1;i<n;i++){
		int a,b;cin>>a>>b;
		v[a].push_back(b);
		v[b].push_back(a);
	}
	dfs(1,0);
	long long ans=1;
	for(int i=1;i<n;i++){
		int a=p[i].first,b=p[i].second;
		a=find(a),b=find(b);
		if(find(b)==find(fa[a][0])||find(fa[b][0])==find(a)){
			ans=((long long)ans*qpow((long long)siz[find(a)]*siz[find(b)]%mod,mod-2))%mod;
			u(a,b);
		}
		else ans=0;
	}
	cout<<ans;
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0);cout.tie(0);
	int t=1;
	//cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

J. Minimum Manhattan Distance

我们将c2的圆心设在原点。由于题目条件x1不等于x2并且y1不等于y2,

b5b1379fe9364000afb8716633224313.png

        所以c1所在的位置一定是在c2上下左右顶点与坐标轴平行直线的外面,并且可以通过关于

坐标轴对称到第一象限,即c2的右上角,此时x1>x2 && y1>y2。我们设c1的圆心是(c,d),

c2内任意一点是(a,b)容易知道对于c2中的一个点到c1内点的期望就是到c1圆心的期望。

证明:

设c1内任意一个点是(x,y),c1的半径为R。

期望等于eq?%5Cfrac%7B%5Csum%20%28x-a&plus;y-b%29%7D%7B%5Cpi%20*R%5E%7B2%7D%7Deq?%5Cleft%20%28%20%28x-c%29%5E%7B2%7D&plus;%28y-d%29%5E%7B2%7D%3C%3DR%5E2%20%5Cright%20%29%29

分子部分即是eq?%5Ciint_%7B%7D%5E%7B%7D%28x-a&plus;y-b%29eq?%5Cleft%20%28%20%28x-c%29%5E%7B2%7D&plus;%28y-d%29%5E%7B2%7D%3C%3DR%5E2%20%5Cright%20%29%29

对于区域不在原点的二重积分我们考虑使用极坐标平移法,

eq?x-c%3Drcos%5Ctheta%20%2Cy-d%3Drsin%5Ctheta

那么eq?x%3Drcos%5Ctheta%20&plus;c%2Cy%3Drsin%5Ctheta%20&plus;d(此处的r为极坐标(r,eq?%5Ctheta)的r)。

此时极点和c1圆心重合,eq?%5Ciint_%7B%7D%5E%7B%7D%28x-a&plus;y-b%29变成eq?%5Cint_%7B0%7D%5E%7B2%5Cpi%20%7D%5Cint_%7B0%7D%5E%7BR%7D%28rcos%5Ctheta%20&plus;rsin%5Ctheta%20&plus;c&plus;d-a-b%29rdrd%5Ctheta

合并一下eq?%5Cint_%7B0%7D%5E%7B2%5Cpi%20%7D%5Cint_%7B0%7D%5E%7BR%7D%28r%5E2%28cos%5Ctheta%20&plus;sin%5Ctheta%29%20&plus;r%28c&plus;d-a-b%29%29drd%5Ctheta

再进行拆分和提取常数项

eq?%5Cint_%7B0%7D%5E%7B2%5Cpi%20%7Dd%5Ctheta%20%28%5Cint_%7B0%7D%5E%7BR%7Dr%5E2%28cos%5Ctheta%20&plus;sin%5Ctheta%29%20dr&plus;%5Cint_%7B0%7D%5E%7BR%7Dr%28c&plus;d-a-b%29%20dr%29

eq?%5Cint_%7B0%7D%5E%7B2%5Cpi%20%7Dd%5Ctheta%20%5Cint_%7B0%7D%5E%7BR%7Dr%5E2%28cos%5Ctheta%20&plus;sin%5Ctheta%29%20dr&plus;%5Cint_%7B0%7D%5E%7B2%5Cpi%20%7Dd%5Ctheta%5Cint_%7B0%7D%5E%7BR%7Dr%28c&plus;d-a-b%29%20dr

前者积分值为0,后者积分值为eq?%28c&plus;d-a-b%29*%5Cpi%20*R%5E2

所以分子积分值为eq?%28c&plus;d-a-b%29*%5Cpi%20*R%5E2

最后再除以分母得c+d-a-b,可知期望值等于c2内一点到c1圆心的曼哈顿距离,证毕。

        当我们知道是到c1圆心期望最小值时,我们就可以进行下一步:找出c2中哪个点

到c1圆心期望最小。

        我们设c2半径为R,c2内一点为(eq?acos%5Ctheta%20%2Casin%5Ctheta),(a<=R)。那么曼哈顿距离就是

c+d-(eq?acos%5Ctheta%20&plus;asin%5Ctheta),c+d为定值,若要曼哈顿距离最小,就是要(eq?acos%5Ctheta%20&plus;asin%5Ctheta

最大。再由辅助角公式可知eq?acos%5Ctheta%20&plus;asin%5Ctheta=eq?%5Csqrt%7B2%7Da*sin%28%5Ctheta%20&plus;%5Cfrac%7B%5Cpi%20%7D%7B4%7D%29,又eq?%5Ctheta%20%5Cepsilon%20%5B0%2C2%5Cpi%20%5D,所以

eq?acos%5Ctheta%20&plus;asin%5Ctheta的最大值为eq?%5Csqrt%7B2%7Da,又a的最大值为R,所以c+d-eq?acos%5Ctheta%20&plus;asin%5Ctheta的最小值

为c+d-eq?%5Csqrt%7B2%7DR。最后的最后,因为c2的圆心不一定在原点,我们将c+d改成两圆的圆心曼哈顿

距离即可,设c2的圆心是(a,b)所以最终结果是c+d-a-b-eq?%5Csqrt%7B2%7DR

K. Minimum Euclidean Distance

        容易知道一个点到圆内点的欧几里得距离平方为eq?%5Cfrac%7B1%7D%7B2%7Dr%5E2&plus;d%5E2,r是圆的半径,d是点到圆心的距离。

证明:

设点为(a,b),圆心为(c,d),圆内任意一点为(x,y),圆的半径是R,期望就是eq?%5Cfrac%7B%5Csum%20%28x-a%29%5E2&plus;%28y-b%29%5E2%7D%7B%5Cpi%20*R%5E2%7D

eq?x%3Drcos%5Ctheta%20&plus;c%2Cy%3Drsin%5Ctheta%20&plus;d

分子即是eq?%5Ciint_%7B%7D%5E%7B%7D%28%28rcos%5Ctheta&plus;c-a%20%29%5E2&plus;%28rsin%5Ctheta%20&plus;d-b%29%5E2%29

将平方拆开合并同类项可得

eq?%5Ciint_%7B%7D%5E%7B%7D%28r%5E3&plus;2*r%5E2*%28c*cos%5Ctheta%20&plus;d*sin%5Ctheta%20-a*cos%5Ctheta%20-b*sin%5Ctheta%20%29&plus;r*%28%28a-c%29%5E2&plus;%28b-d%29%5E2%29%29drd%5Ctheta

再拆成多个二重积分之和

eq?%5Ciint_%7B%7D%5E%7B%7D%28r%5E3%29drd%5Ctheta%20&plus;%5Ciint_%7B%7D%5E%7B%7D%282*r%5E2*%28c*cos%5Ctheta%20&plus;d*sin%5Ctheta%20-a*cos%5Ctheta%20-b*sin%5Ctheta%20%29%29drd%5Ctheta%20&plus;%5Ciint_%7B%7D%5E%7B%7D%28r*%28%28a-c%29%5E2&plus;%28b-d%29%5E2%29%29drd%5Ctheta

且他们的二重积分上下值都是eq?%5Cint_%7B0%7D%5E%7B2%5Cpi%20%7D%5Cint_%7B0%7D%5E%7BR%7D,然后算出三个积分值为eq?%5Cfrac%7B2*%5Cpi%20*R%5E4%7D%7B4%7D,0,eq?%5Cfrac%7B2*%5Cpi%20*R%5E2*%28%28a-c%29%5E2&plus;%28b-d%29%5E2%29%7D%7B2%7D,最后分子除以分母得eq?%5Cfrac%7BR%5E2%7D%7B2%7D&plus;%28a-c%29%5E2&plus;%28b-d%29%5E2

eq?%5Cfrac%7B1%7D%7B2%7Dr%5E2&plus;d%5E2,证毕。

        分类讨论,若圆心在凸包内部则令d为0;若圆心在凸包外部,则遍历凸包的边求

圆心到这些边距离的最小值即可。对于如何判断是否在凸包内部,则逆时针遍历边,用叉积判断凸包的每条边圆心是否在这些边的左边即可。

        叉积中若a*b值为正表示a在b的顺时针方向。求投影可以用a.b=|a|*|b|*cos\theta,由投影判断

点与线段位置关系。求垂线距离可以用叉积求出平行四边形面积再除以底边长。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=3e6+10;
const int inf=0x3f3f3f3f;
typedef long long ll;
typedef pair<int,int> pii;
typedef unsigned long long ull;
//const ll P=2281701377;
const ll P=998244353;

struct point{
    double x,y;
};
struct vec{
    double x,y;
};
double cross(vec a,vec b){
    return a.x*b.y-a.y*b.x;
}
double get_dis(point a,point b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double get_point_mul(vec a,vec b){
    return a.x*b.x+a.y*b.y;
}
vec get_vector(point a,point b){
    vec res={b.x-a.x,b.y-a.y};
    return res;
}
double get_area(point a,point b,point c){
    vec vecb=get_vector(a,b),vecc=get_vector(a,c);
    return fabs(cross(vecb,vecc));
}
bool get_pos(point a,point b,point c){
    double modlenb=get_dis(a,b),modlenc=get_dis(a,c);
    vec vecb=get_vector(a,b),vecc=get_vector(a,c);
    double prolen=get_point_mul(vecb,vecc)/modlenb;
    return (prolen>=0&&prolen<=modlenb)?1:0;
}
double get_line_point(point a,point b,point c){
    if(get_pos(a,b,c)){
        double s=get_area(a,b,c);
        double h=s/get_dis(a,b);
        return h;
    }
    return min(get_dis(a,c),get_dis(b,c));
}
int get_direction(point a,point b,point c){
    vec vecb=get_vector(a,b),vecc=get_vector(a,c);
    double mulres=cross(vecb,vecc);
    return (mulres<=0)?1:0;
}
int n,q;
point p[20000];
void solve(){
    cin>>n>>q;
    for(int i=1;i<=n;i++){
        int x,y;cin>>x>>y;
        p[i].x=x,p[i].y=y;
        p[i+n]=p[i];
    }
    while(q--){
        int a,b,c,d;
        cin>>a>>b>>c>>d;
        int f=0;
        point cir={(a+c)*1.0/2,(b+d)*1.0/2};
        double r=get_dis({a,b},{c,d})/2;
        double ans=r*r/2;
        for(int i=n+1;i>=2;i--){
            if(!get_direction(p[i],p[i-1],cir)){
                f=1;break;
            }
        }
        if(!f){
            cout<<fixed<<setprecision(8)<<ans<<endl;continue;
        }
        double minn=1.0*0x3f3f3f3f3f3f3f;
        for(int i=1;i<=n;i++){
            double d=get_line_point(p[i],p[i+1],cir);
            minn=min(minn,d);
        }
        cout<<fixed<<setprecision(8)<<ans+minn*minn<<endl;
    }
}
int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    //cin>>t;
    while(t--){
        solve();
    }

}

  • 25
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值