IOI2020集训队作业-6 (CF571E, CF696F, ARC093E)

A - CF571E Geometric Progressions

题意

给出 n n n个数列,第 i i i个数列是: a i , a i b i , a i b i 2 , a i b i 3 ⋯ a_i,a_ib_i,a_ib_i^2 ,a_ib_i^3 \cdots ai,aibi,aibi2,aibi3

问最小的被每一个数列都包含了的数对 1 0 9 + 7 10^9+7 109+7取模的值。无解输出 − 1 -1 1

1 ≤ n ≤ 100 , 1 ≤ a i , b i ≤ 1 0 9 1\le n\le 100,1\le a_i,b_i\le 10^9 1n100,1ai,bi109

Sol

两个等比数列的交,要么也是一个等比数列(公比可能为 1 1 1),要么为空集。

设第一个等比数列的第 x 1 x_1 x1项和第二个等比数列的第 x 2 x_2 x2项相同( x 1 , x 2 ≥ 0 x_1,x_2\ge 0 x1,x20),则有

a 1 b 1 x 1 = a 2 b 2 x 2 a_1 b_1^{x_1} = a_2b_2^{x_2} a1b1x1=a2b2x2

对每一个质因子进行考虑,设质因子 p p p m m m中的次数为 e p ( m ) e_p(m) ep(m),则:

e p ( a 1 ) + e p ( b 1 ) x 1 = e p ( a 2 ) + e p ( b 2 ) x 2 e_{p}(a_1) + e_p(b_1) x_1 = e_p(a_2)+e_p(b_2)x_2 ep(a1)+ep(b1)x1=ep(a2)+ep(b2)x2

这样我们将得到若干个关于 x 1 x_1 x1 x 2 x_2 x2的二元一次方程。

这个方程组要么无解,要么有无数组解,要么有唯一解。对应的就是等比数列的交集为空,交集为公比不为 1 1 1的等比数列,交集为公比为 1 1 1的等比数列。

每次合并两个等比数列,直到最终只剩下一个等比数列,输出剩下的等比数列的首项就可以了。

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
	x=0; char c=getchar(); int f=1;
	while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
	while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
inline ll Abs(ll x) { return x>0?x:-x; }
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a; }
struct item {
	int p; ll k;
	item(int p=0,ll k=0): p(p),k(k) {}
};
struct Num {
	item p[810];
	int num;
	void init(int x) {
		for(int i=2;i*(ll)i<=x;++i) if(x%i==0) {
			int c=0;
			while(x%i==0) x/=i,c++;
			p[num++]=item(i,c);
		}
		if(x>1) p[num++]=item(x,1);
	}
	item query(int x) {
		for(int i=0;i<num;++i) if(p[i].p==x) return p[i];
		return item(x,0);
	}
}A[110],B[110];
int n;
void FAIL() { puts("-1"); exit(0); }

item A1[3210],B1[3210],A2[3210],B2[3210];
int pri[3210],num;
ll exgcd(ll a,ll b,ll &x,ll &y) {
	if(!b) { x=1,y=0; return a; }
	ll g=exgcd(b,a%b,y,x); y-=a/b*x; return g;
}
void Inter(ll A,ll B,ll C,ll a,ll b,ll c,ll &x1,ll &x2) {
	while(a) {
		ll t=A/a;
		A-=t*a,B-=t*b,C-=t*c;
		swap(A,a),swap(B,b),swap(C,c);
	}
	if(c%b) FAIL();
	x2=-c/b;
	if((C+B*x2)%A) FAIL();
	x1=(-C-B*x2)/A;
}
void UN(Num &a1,Num &b1,Num &a2,Num &b2) {
	num=0;
	for(int i=0;i<a1.num;++i) pri[num++]=a1.p[i].p;
	for(int i=0;i<a2.num;++i) pri[num++]=a2.p[i].p;
	for(int i=0;i<b1.num;++i) pri[num++]=b1.p[i].p;
	for(int i=0;i<b2.num;++i) pri[num++]=b2.p[i].p;
	sort(pri,pri+num);
	num=unique(pri,pri+num)-pri;
	for(int i=0;i<num;++i) A1[i]=a1.query(pri[i]);
	for(int i=0;i<num;++i) A2[i]=a2.query(pri[i]);
	for(int i=0;i<num;++i) B1[i]=b1.query(pri[i]);
	for(int i=0;i<num;++i) B2[i]=b2.query(pri[i]);
	
	ll A=0,B=0,C=0;
	int flg1=0; ll x1,x2;
	for(int i=0;i<num;++i) {
		ll a=B1[i].k,b=-B2[i].k,c=A1[i].k-A2[i].k;
		if(a==0&&b==0) {
			if(c) FAIL();
			continue;
		}
		ll g=gcd(a,gcd(-b,Abs(c)));
		a/=g,b/=g,c/=g;
		if(!b) {
			if(c%a) FAIL();
			if(-c/a<0) FAIL();
		}
		if(!A&&!B) { A=a,B=b,C=c; continue; }
		if(!B) {
			if(b) { Inter(A,B,C,a,b,c,x1,x2),flg1=1; break; }
			if(C/A!=c/a) FAIL();
			continue;
		}
		if(A*b==a*B) {
			if(c*A==C*a) continue;
			FAIL();
		}
		Inter(A,B,C,a,b,c,x1,x2),flg1=1;
		break;
	}
	if(flg1) {
		for(int i=0;i<num;++i) {
			ll a=B1[i].k,b=-B2[i].k,c=A1[i].k-A2[i].k;
			if(a*x1+b*x2+c) FAIL();
		}
		for(int i=0;i<num;++i) {
			A1[i].k=A1[i].k+B1[i].k*x1;
			B1[i].k=0;
		}
		for(int i=0;i<num;++i)
			a1.p[i]=A1[i],b1.p[i]=B1[i];
		a1.num=b1.num=num;
		return;
	}
	ll g=exgcd(A,B,x1,x2);
	
	if(C%g) FAIL();
	x1*=-C/g,x2*=-C/g;
	
	ll tx=Abs(-B/g),ty=Abs(A/g);
	if(C>0||!ty) {
		x1=(x1%tx+tx)%tx;
		if(B) x2=-(A*x1+C)/B;
		else x2=0;
	}
	else {
		x2=(x2%ty+ty)%ty;
		if(A) x1=(-B*x2-C)/A;
		else x1=0;
	}
	for(int i=0;i<num;++i) {
		A1[i].k=A1[i].k+B1[i].k*x1;
		B1[i].k=tx*B1[i].k;
	}
	for(int i=0;i<num;++i)
		a1.p[i]=A1[i],b1.p[i]=B1[i];
	a1.num=b1.num=num;
}
const int mod=1e9+7;
int Pow(int x,ll y) {
	int res=1;
	for(y%=(mod-1);y;x=x*(ll)x%mod,y>>=1)
		if(y&1) res=res*(ll)x%mod;
	return res;
}
int main() {
	rd(n);
	for(int i=1,a,b;i<=n;++i) rd(a),rd(b),A[i].init(a),B[i].init(b);
	for(int i=2;i<=n;++i) UN(A[1],B[1],A[i],B[i]);
	long long ans=1;
	for(int i=0;i<A[1].num;++i)
		ans=ans*(ll)Pow(A[1].p[i].p,A[1].p[i].k)%mod;
	printf("%lld",ans);
	return 0;
}

B - CF696F …Dary!

题意

有一个包含 n n n个顶点的凸多边形。

你需要在凸多边形内选两个点,以这两个点为圆心、以某个实数 r r r为半径画两个圆,使得凸多边形的每一条边所在直线至少与一个圆有交。

你需要最小化 r r r

n ≤ 300 n\le 300 n300,坐标绝对值不超过 1 0 4 10^4 104,答案要求精确到 1 0 − 6 10^{-6} 106

Sol

首先二分 r r r

如果圆与某条边所在直线有交,那么这个圆的圆心一定在这条边向多边形的中心平移 r r r之后得到的直线所划分出的、异于平移方向的那个半平面内。

所以,可以在多边形找出两个满足条件的圆心,等价于能够把这样的 n n n个半平面分成至多两组,每组和多边形的交不为空集。

一种暴力的做法是:圆心一定可以是某两个半平面的交点,我们不妨去枚举这个交点 p p p,然后删掉所有与它有交的半平面,检查剩下的半平面的交与多边形的交是否为空集。这样的复杂度是 O ( n 3 ) O(n^3) O(n3)的。

可以证明,我们的决策一定是将原多边形上连续的一段边对应的半平面分在一组。

考虑 l l l这条边和 r r r这条边所对应的半平面的交点 p p p,如果在 l l l r r r之间的存在一条边对应的半平面与 p p p没有交,并且在 r r r l l l之间也存在一条边对应的半平面与 p p p没有交,那么 p p p一定不能作为圆心,这是因为那两个半平面与多边形的交一定是空集。

所以可以考虑对于每条边求出一个最大的 l l l,满足它和它之后的 l l l条边对应的半平面的与多边形的交不为空集。 l l l显然具有单调性,可以双指针。

时间复杂度 O ( n 2 log ⁡ V ) O(n^2\log V) O(n2logV)

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define ll long long
#define db long double
using namespace std;
template <class T>
inline void rd(T &x) {
	x=0; char c=getchar(); int f=1;
	while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
	while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
const db eps=1e-9;
const int N=310;
int sgn(db x) { return x>-eps?(x>eps):-1; }
struct Point {
	db x,y;
	Point(db x=0,db y=0): x(x),y(y) {}
	friend Point operator +(Point A,Point B) { return Point(A.x+B.x,A.y+B.y); }
	friend Point operator -(Point A,Point B) { return Point(A.x-B.x,A.y-B.y); }
	friend Point operator *(Point A,db B) { return Point(A.x*B,A.y*B); }
	friend Point operator /(Point A,db B) { return Point(A.x/B,A.y/B); }
};
db Cross(Point A,Point B) { return A.x*B.y-A.y*B.x; }
db len(Point A) { return sqrt(A.x*A.x+A.y*A.y); }
struct Line {
	Point u,v;
	Line(Point u=Point(0,0),Point v=Point(0,0)): u(u),v(v) {}
};
Point Inter(Line A,Line B) {
    db s1=Cross(B.u-A.u,A.v-A.u);
    db s2=Cross(A.v-A.u,B.v-A.u);
    return (B.u*s2+B.v*s1)/(s1+s2);
}
int n;
Line p[N<<1];
int rnk[N<<1];
int pos[N<<1];
int vis[N<<1];
db ag[N<<1];
bool cmpag(int x,int y) { return ag[x]<ag[y]; }
bool HPI(int l,int r,int flg) {
	static Line q[N<<1];
	static Point s[N<<1];
	static int id[N<<1];
	for(int i=l;i!=r;++i) vis[i%n+n]=1;
	int hd=1,tl=0;
	for(int i=0;i<n*2;++i) if(vis[pos[i]]) {
		Line c=p[pos[i]];
		while(hd<tl&&sgn(Cross(c.v-c.u,s[tl]-c.u))<0) tl--;
		while(hd<tl&&sgn(Cross(c.v-c.u,s[hd+1]-c.u))<0) hd++;
		if(hd<=tl&&sgn(Cross(c.v-c.u,q[tl].v-q[tl].u))==0) {
			if(sgn(Cross(c.v-c.u,q[tl].v-c.u))<0) {
				if(sgn(Cross(q[tl].v-q[tl].u,c.v-q[tl].u))>=0)
					q[tl]=c,id[tl]=pos[i];
				else {
					for(int i=l;i!=r;++i) vis[i%n+n]=0;
					return 0;
				}
			}
		}		
		else q[++tl]=c,id[tl]=pos[i];
		if(hd<tl) s[tl]=Inter(q[tl],q[tl-1]);
	}
	while(hd<tl&&sgn(Cross(q[hd].v-q[hd].u,s[tl]-q[hd].u))<0) tl--;
	for(int i=l;i!=r;++i) vis[i%n+n]=0;
	if(tl-hd+1<3) return 0;
	s[hd]=Inter(q[hd],q[tl]);
	if(flg) printf("%.10Lf %.10Lf\n",s[hd].x,s[hd].y);
	return 1;
}
Point P[N];
bool check(db d) {
	for(int i=0;i<n;++i) {
		Point t=p[i].v-p[i].u;
		Point r=Point(-t.y,t.x);
		r=r*(d/len(p[i].v-p[i].u));
		p[i+n].u=p[i].v+r;
		p[i+n].v=p[i].u+r;
	}
	int r=1;
	for(int i=0;i<n;++i) {
		r=max(r,i+1);
		while(r+1<i+n&&HPI(i,r+1,0)) r++;
		if(HPI(r,i+n,0)) return 1;
	}
	return 0;
}
void sol(db d) {
	printf("%.10Lf\n",d);
	for(int i=0;i<n;++i) {
		Point t=p[i].v-p[i].u;
		Point r=Point(-t.y,t.x);
		r=r*(d/len(p[i].v-p[i].u));
		p[i+n].u=p[i].v+r;
		p[i+n].v=p[i].u+r;
	}
	int r=1;
	for(int i=0;i<n;++i) {
		r=max(r,i+1);
		while(r+1<i+n&&HPI(i,r+1,0)) r++;
		if(HPI(r,i+n,0)) {
			HPI(i,r,1);
			HPI(r,i+n,1);
			return;
		}
	}
}
int main() {
	rd(n);
	for(int i=0;i<n;++i) scanf("%Lf%Lf",&P[i].x,&P[i].y);
	p[0]=Line(P[n-1],P[0]);
	for(int i=1;i<n;++i) p[i]=Line(P[i-1],P[i]);
	for(int i=0;i<n;++i) p[i+n]=p[i],swap(p[i+n].u,p[i+n].v);
	for(int i=0;i<2*n;++i) ag[i]=atan2(p[i].v.y-p[i].u.y,p[i].v.x-p[i].u.x),pos[i]=i;
	sort(pos,pos+2*n,cmpag);
	for(int i=0;i<2*n;++i) rnk[pos[i]]=i;
	for(int i=0;i<n;++i) vis[i]=1;
	db l=0,r=1e5;
	while(r-l>1e-7) {
		db mid=(l+r)/2;
		if(check(mid)) r=mid;
		else l=mid;
	}
	sol(r);
	return 0;
}

C - ARC093E Bichrome Spanning Tree

题意

n n n个点、 m m m条边的带权无向图。问有多少种把边黑白染色的方式,使得这张图的同时包含了两种颜色的边的生成树,边权和最小为 X X X。答案对 1 0 9 + 7 10^9+7 109+7取模。

n ≤ 1000 , m ≤ 2000 , 1 ≤ w ≤ 1 0 9 , X ≤ 1 0 12 n\le 1000,m\le 2000,1\le w \le 10^9,X\le 10^{12} n1000,m2000,1w109,X1012 w w w是边权)

Sol

首先求出最小生成树的边权 W W W,设 Δ = X − W \Delta = X - W Δ=XW

如果 Δ < 0 \Delta < 0 Δ<0则无解。

考虑如果最小生成树中所有边都是同一种颜色,我们肯定是从图中另选一条与树边颜色不同的边,然后断掉这条边连接的点在树上的路径之间边权最大的边。

对于每条不在最小生成树上的边求出(这条边的边权 - 这条边连接的点在树上的路径之间的边的最大边权),设 x 0 , x 1 , x 2 x_0,x_1,x_2 x0,x1,x2分别表示这个值小于 Δ \Delta Δ,等于 Δ \Delta Δ,大于 Δ \Delta Δ的边的数量。

如果 Δ = 0 \Delta = 0 Δ=0,那么要么原来的最小生成树就合法,要么最小生成树不合法、但 x 1 x_1 x1中有边与最小生成树上的边的颜色不一样。答案就是 ( 2 n − 1 − 2 ) ⋅ 2 m − ( n − 1 ) + 2 ⋅ ( 2 x 1 − 1 ) ⋅ 2 x 2 (2^{n-1}-2)\cdot 2^{m-(n-1)} + 2\cdot (2^{x_1}-1)\cdot 2^{x_2} (2n12)2m(n1)+2(2x11)2x2

如果 Δ > 0 \Delta > 0 Δ>0,那么就要求原来的最小生成树不合法,并且所有 x 0 x_0 x0的边都与最小生成树上的边的颜色相同,并且存在 x 1 x_1 x1的边与最小生成树上的边颜色不同。答案就是 2 ⋅ ( 2 x 1 − 1 ) 2 x 2 2\cdot (2^{x_1}-1)2^{x_2} 2(2x11)2x2

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
	x=0; char c=getchar(); int f=1;
	while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
	while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
const int mod=1e9+7;
int Pow(int x,int y) {
	int res=1;
	while(y) {
		if(y&1) res=res*(ll)x%mod;
		x=x*(ll)x%mod,y>>=1;
	}
	return res;
}
const int N=1010,M=2010;
int head[N],ecnt;
struct ed { int to,next,w; }e[N<<1];
void ad(int x,int y,int w) {
	e[++ecnt]=(ed){y,head[x],w}; head[x]=ecnt;
	e[++ecnt]=(ed){x,head[y],w}; head[y]=ecnt;
}
int gl,qmx;
void dfs(int u,int last,int mx) {
	if(u==gl) { qmx=mx; return; }
	for(int k=head[u];k;k=e[k].next) {
		int v=e[k].to; if(v==last) continue;
		dfs(v,u,max(mx,e[k].w));
	}
}
int Query(int x,int y) {
	gl=y;
	dfs(x,0,0);
	return qmx;
}
struct edge {
	int x,y,w;
	friend bool operator < (edge A,edge B) { return A.w<B.w; }
}E[M];
int used[M];
int fa[N];
int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); }
bool un(int x,int y) {
	if(find(x)==find(y)) return 0;
	fa[find(x)]=find(y); return 1;
}
ll X;
int n,m;
int main() {
	rd(n),rd(m),rd(X);
	for(int i=1;i<=m;++i) rd(E[i].x),rd(E[i].y),rd(E[i].w);
	sort(E+1,E+m+1);
	for(int i=1;i<=n;++i) fa[i]=i;
	ll ans=0;
	for(int i=1;i<=m;++i)
		if(un(E[i].x,E[i].y)) ad(E[i].x,E[i].y,E[i].w),ans+=E[i].w,used[i]=1;
	int cnt0=0,cnt1=0,cnt2=0;
	ll del=X-ans;
	if(del<0) {
		printf("0"); return 0;
	}
	for(int i=1;i<=m;++i) if(!used[i]) {
		int t=E[i].w-Query(E[i].x,E[i].y);
		if(t>del) cnt2++;
		else if(t==del) cnt1++;
		else cnt0++;
	}
	if(del==0)
		printf("%d\n",((Pow(2,n-1)-2)*(ll)Pow(2,m-(n-1))%mod+2ll*(Pow(2,cnt1)-1)*Pow(2,cnt2)%mod+mod)%mod);
	else
		printf("%d\n",(2ll*Pow(2,cnt2)*(ll)(Pow(2,cnt1)-1)%mod+mod)%mod);
	return 0;
}
E[i].w,used[i]=1;
	int cnt0=0,cnt1=0,cnt2=0;
	ll del=X-ans;
	if(del<0) {
		printf("0"); return 0;
	}
	for(int i=1;i<=m;++i) if(!used[i]) {
		int t=E[i].w-Query(E[i].x,E[i].y);
		if(t>del) cnt2++;
		else if(t==del) cnt1++;
		else cnt0++;
	}
	if(del==0)
		printf("%d\n",((Pow(2,n-1)-2)*(ll)Pow(2,m-(n-1))%mod+2ll*(Pow(2,cnt1)-1)*Pow(2,cnt2)%mod+mod)%mod);
	else
		printf("%d\n",(2ll*Pow(2,cnt2)*(ll)(Pow(2,cnt1)-1)%mod+mod)%mod);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值