测试题--by 罗雨屏

T1:

就是给你平面上的n个点,要你求个最小方差生成树

这题完全不会做,搜了题解才恍然大悟,简直巧妙

首先假设知道选的边权的平均数是a,那么就把新边权定位(v-a)^2(v是原来的边权),再做一遍最小生成树就行了

然后我们发现,影响最小生成树的不是边权,而是边权的相对大小关系(从kruskal上很好理解)

想象两条边v1,v2,当a从<(v1+v2)/2变化到a>(v1+v2)/2时,这两条边的大小关系发生变化,于是把m^2个变化点搞出来,分别做一遍最小生成树就Ok了

复杂度O(m^2*n^2)

#include<cmath>
#include<cstdio>
#include<cstring>
#include<cassert>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=25;
int n,x[maxn],y[maxn];
void init(){
	scanf("%d",&n);
	for (int i=1;i<=n;++i) scanf("%d",x+i);
	for (int i=1;i<=n;++i) scanf("%d",y+i);
}
double e[maxn*maxn];
double w[maxn*maxn*maxn*maxn];
double ans;
void gao(double d[]){
	double res=0;
	for (int i=2;i<=n;++i) res+=d[i];
	res/=n-1; double s=0;
	for (int i=2;i<=n;++i) s+=(d[i]-res)*(d[i]-res);
	s=s/(n-1); if (s<ans) ans=s;
}
inline double getdist(int a,int b){
	return sqrt(1.0*(x[a]-x[b])*(x[a]-x[b])+1.0*(y[a]-y[b])*(y[a]-y[b]));
}
void prim(double x){
	static double d[maxn],dist[maxn]; static bool vis[maxn];
	memset(vis,0,sizeof(vis)); for (int i=0;i<=n;++i) dist[i]=1e20;
	dist[1]=0;
	for (int i=1;i<=n;++i){
		int tmp=0; for (int i=1;i<=n;++i) if (!vis[i] && dist[i]<dist[tmp]) tmp=i;
		vis[tmp]=1;
		for (int i=1;i<=n;++i) if (!vis[i]){
			double res=getdist(tmp,i)-x;
			if (res*res<dist[i]){
				dist[i]=res*res; d[i]=getdist(tmp,i);
			}
		}
	}
	gao(d);
}
void work(){
	int m=0; ans=1e20;
	for (int i=1;i<=n;++i)
		for (int j=i+1;j<=n;++j) e[++m]=getdist(i,j);
	int t=0; 
	for (int i=1;i<=m;++i) 
		for (int j=i+1;j<=m;++j) w[++t]=(e[i]+e[j])/2;
	sort(w+1,w+t+1); prim(0);
	for (int i=1;i<t;++i) prim((w[i]+w[i+1])/2); prim(w[t]+1);
	printf("%.3f\n",sqrt(ans));
}
int main(){
	freopen("mst.in","r",stdin); freopen("mst.out","w",stdout);
	int T; scanf("%d",&T);
	while (T--) init(),work();
	return 0;
}

T2:

有一只狐狸在原点上,方向朝着x轴正方向,每过一单位时间,每只狐狸都会分裂成(S+L+R)只,其中S只前进一步,L只原地左转,R只原地右转,每只狐狸的贡献是它所在的坐标的横纵坐标的积,要你求t时间后,所有狐狸的贡献是多少    S,L,R<=10^9   t<=10^18  还有多组数据,组数<=30

又是一道蘑菇题。。。

维护一个13*13的矩阵,分别代表方向为上下左右的sigma(x),sigma(y),cnt,还有一个记录当前的总贡献,这样矩阵乘法就Ok了,至于转移什么的很简单,就是很多。。。

#include<vector>
#include<cstdio>
#include<cassert>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long ll;
//const int dx[]={1,0,-1,0},dy[]={0,-1,0,1};
const int mod=1000000007;
ll n; int S,L,R;
struct Tmx{
	ll a[14][14];
	void clear(){memset(a,0,sizeof(a));}
	void xxx(){
		clear();
		a[0][0]=S; a[3][0]=L; a[9][0]=R;
		a[1][1]=S; a[2][1]=S; a[4][1]=L; a[10][1]=R;
		a[2][2]=S; a[5][2]=L; a[11][2]=R;
		a[0][3]=R; a[3][3]=S; a[5][3]=S; a[6][3]=L;
		a[1][4]=R; a[4][4]=S; a[7][4]=L;
		a[2][5]=R; a[5][5]=S; a[8][5]=L;
		a[3][6]=R; a[6][6]=S; a[9][6]=L;
		a[4][7]=R; a[7][7]=S; a[8][7]=-S; a[10][7]=L;
		a[5][8]=R; a[8][8]=S; a[11][8]=L;
		a[0][9]=L; a[6][9]=R; a[9][9]=S; a[11][9]=-S;
		a[1][10]=L; a[7][10]=R; a[10][10]=S;
		a[2][11]=L; a[8][11]=R; a[11][11]=S;
		a[0][12]=S; a[4][12]=S; a[6][12]=-S; a[10][12]=-S;
		a[12][12]=(0LL+S+L+R)%mod;
	}
}I;
void mul(Tmx &c,Tmx b){
	Tmx a=c; c.clear();
	for (int i=0;i<13;++i) for (int j=0;j<13;++j)
		for (int k=0;k<13;++k) c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%mod;
}
Tmx power(Tmx a,ll t){
	Tmx res=I;
	for (;t;t>>=1,mul(a,a))
		if (t&1) mul(res,a);
	return res;
}
int main(){
	freopen("fox.in","r",stdin); freopen("fox.out","w",stdout);
	I.clear(); for (int i=0;i<14;++i) I.a[i][i]=1;
	int T; cin>>T;
	while (T--){
		cin>>n>>S>>L>>R;
		Tmx a; a.xxx(); a=power(a,n);
		Tmx b; b.clear(); b.a[0][5]=1; mul(b,a);
		printf("%d\n",(b.a[0][12]%mod+mod)%mod);
	}


T3:

给你一个2*m的格子,每个格子需要填进1,2,3中的一个,要求相同的数字不能相邻,且每个2*2的格子中3个数都必须有,还限定了1,2,3的个数a,b,c  保证a+b+c=2*m

这题很巧妙(碰巧今天ZJOI的讲课上也讲了),首先意识到前一列确定了,那么自己这一列就还剩下一个必须选的,如果我们知道了每一列必须选的是谁,那么我们就可以构造出两种原染色方案(且不重不漏),于是问题变成用1,2,3填一列数,相同的不相邻的方案数(1,2,3的个数任然确定,用原来的接一下方程就知道了)

接下来就有很多种方法了,说的可能不是很清楚  去膜拜 http://blog.csdn.net/maverick1990/article/details/16954151 吧

#include<cmath>
#include<cstdio>
#include<cassert>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int mod=1000000007,maxm=1000011;
int m,R,G,B;
void init(){
	scanf("%d%d%d%d",&m,&R,&G,&B);
}
int power(int x,int t){
	int res=1;
	for (;t;t>>=1,x=1LL*x*x%mod)
		if (t&1) res=1LL*res*x%mod;
	return res;
}
int fact[maxm],nfac[maxm];
inline int C(int n,int m){
	return 1LL*fact[n]*nfac[m]%mod*nfac[n-m]%mod;
}
int Query(int x,int y,int z){
	if (!x && !y && !z) return 1;
	if (!x || z>x+y) return 0;
	int res=0; int lim=min(x,y);
	for (int k=0;k<=lim;++k){
		if (!k && y) continue;
		if (z<(x-k)+(y-k)) continue;
		int a=C(x,k),b,c;
		if (!y) b=1; if (k>=1) b=C(y-1,k-1);
		c=C(2*k,z-(x-k)-(y-k));
		res=(res+1LL*a*b%mod*c%mod)%mod;
	}
	return res;
}
void work(){
	fact[0]=1; for (int i=1;i<=m;++i) fact[i]=1LL*fact[i-1]*i%mod;
	nfac[m]=power(fact[m],mod-2);
	for (int i=m-1;i>=0;--i) nfac[i]=nfac[i+1]*(i+1LL)%mod;
	
	int x=m-R,y=m-B,z=m-G;
	if (x<0 || y<0 || z<0){puts("0"); return;}
	if (x<y) swap(x,y); if (x<z) swap(x,z); if (y<z) swap(y,z); // z<=y<=x
	int res=(0LL+Query(x+1,y,z)+Query(x,y,z)*2+Query(x-1,y,z))%mod;
	printf("%d\n",(res+res)%mod);
}
int main(){
	freopen("board.in","r",stdin); freopen("board.out","w",stdout);
	int T; cin>>T;
	while (T--) init(),work();
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值