TopCoder SRM 566

div.1

T1

题意 :

有n个点,m条边,点的位置不知道,但是三点不共线,可以去掉任意条边,问使得没有两条线相交的情况的数量;
思想:
有两种选法,一种是三条边正好围成三角形,另一种是菊花图,这样就保证最长的链长度小于等于2。。。

#include <bits/stdc++.h>
using namespace std;
long long a[100][100],b[100];
class PenguinSledding {
public:
long long countDesigns( int numCheckpoints, vector <int> checkpoint1, vector <int> checkpoint2 ) ;
};
long long sol(long long x,long long y)
{
	long long s=1;
	while(y>0)
	{
		if(y&1) s=s*x;
		x=x*x;
		y=y>>1;
	}
	return s;
}
long long PenguinSledding::countDesigns(int numCheckpoints, vector <int> checkpoint1, vector <int> checkpoint2) {
	long long ans=0;
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	int i,j,k;
	for(i=0;i<checkpoint1.size();i++)
	{
		if(a[checkpoint1[i]][checkpoint2[i]]==0)
		{
			a[checkpoint1[i]][checkpoint2[i]]=a[checkpoint2[i]][checkpoint1[i]]=1;
			b[checkpoint1[i]]++;
			b[checkpoint2[i]]++;
		}
	}
	for(i=1;i<=numCheckpoints;i++)
		for(j=i+1;j<=numCheckpoints;j++)
			for(k=j+1;k<=numCheckpoints;k++)
				if(a[i][j]&&a[j][k]&&a[k][i]) ans++;
	for(i=1;i<=numCheckpoints;i++)
		ans=ans+sol(2,b[i])-1;
	ans-=checkpoint1.size()-1;
	return ans;
}
T2

题意

有一个环,你刚开始在0好点,环上一共有n个点,每天你可以顺时针或逆时针走,在第i天你可以走i步,给出n和天数k,(n<350&&k<1e18),问k天后你在0号点的方案数。

思路

我们考虑在天数大于n后,每天走的步数相当于k%n步,然后这样子我们发现n天一个循环,然后每n天我们走路转移的状态是一样的,我们可以从第n天的状态,推出2n天的状态。。。然后这时我们就可以用快速幂来处理这个k了,复杂度是O(n^2*logk)

#include <bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
class PenguinEmperor {
public:
int countJourneys( int numCities, long long daysPassed ) ;
};
vector<long long> sol(vector<long long> a,vector<long long> b)
{
	int n=a.size(),i,j;
	vector<long long> c(n,0);
	for(i=0;i<n;i++)
		for(j=0;j<n;j++)
			c[(i+j)%n]=(c[(i+j)%n]+a[i]*b[j])%mod;
    return c;
}
vector<long long> poww(vector<long long> a,long long k)
{
	if(k==0)
	{
		a=vector<long long>(a.size(),0);
		a[0]=1;
		return a;
	}
	if(k%2==0) return poww(sol(a,a),k/2);
	else return sol(a,poww(a,k-1));
}
int PenguinEmperor::countJourneys(int numCities, long long daysPassed) {
	vector<long long> x(numCities,0);
	vector<long long> y(numCities,0);
	x[0]=y[0]=1;
	for(int i=1;i<numCities;i++)
	{
		vector<long long> w(numCities,0);
		w[i]=w[numCities-i]=1;
		x=sol(w,x);
		if(i<=daysPassed%numCities) y=sol(w,y);
	}
	vector<long long> ans=sol(y,poww(x,daysPassed/numCities));
	return (int)ans[0]%mod;
}

div.2

T3

题意

给定n个栅栏每个栅栏与原点的距离都为r,第一个栅栏坐标为(r,0),其余栅栏均匀排布为一正多边形。
现有p个企鹅,你需要通过连线栅栏形成一个封闭图形,将这些企鹅全部包含在内。求此围栏的最小面积,无法完成输出-1。

思路

首先我们先考虑如何判断企鹅和多边形一条边的位置关系。
较为简单的一种方法是考虑直线的方向,如果直线、企鹅成逆时针,则说明在多边形内侧。
判断逆时针可以采用向量的叉积。
而计算最终结果很显然就是一个区间dp了,每次引入的新点带来的面积增量就是个三角形,用向量算算就好了。注意最终点还要检验能否连回第一个点。

#include <bits/stdc++.h>
using namespace std;
const double pi=acos(-1.0);
const double inf=1e9;
double dp[250][250];
double dx[250],dy[250];
vector<int> X,Y;
class FencingPenguinsEasy
{
	public:
		double calculateMinArea( int numPosts, int radius, vector <int> x, vector <int> y ) ;
};
int check(double x1,double y1,double x2,double y2)
{
	for(int i=0; i<X.size(); i++)
	{
		int x3=X[i],y3=Y[i];
		double t=1.0*(x1-x3)*(y2-y1)-1.0*(y1-y3)*(x2-x1);
		if(t<0) return 0;
	}
	return 1;
}
double sol(double x1, double y1, double x2, double y2, double x3, double y3)
{
	x1=x1-x3;
	y1=y1-y3;
	x2=x2-x3;
	y2=y2-y3;
	return abs((x1*y2-x2*y1)/2.0);
}
double FencingPenguinsEasy::calculateMinArea(int numPosts, int radius, vector <int> x, vector <int> y)
{
	int i,j,k;
	double c=2.0*pi/numPosts,ans=inf;
	for(i=0; i<x.size(); i++) X.push_back(x[i]);
	for(i=0; i<y.size(); i++) Y.push_back(y[i]);
	for(i=0; i<numPosts; i++)
	{
		dx[i]=(radius*cos(c*i));
		dy[i]=(radius*sin(c*i));
	}
	for(i=1; i<numPosts; i++)
		if(!check(dx[i-1],dy[i-1],dx[i],dy[i]))
			return -1.0;
	if(!check(dx[numPosts-1],dy[numPosts-1],dx[0],dy[0]))
		return -1.0;

	for(i=0; i<numPosts; i++)
	{
		for(j=0; j<numPosts; j++)
		{
			if(i==j) dp[i][j]=0;
			else dp[i][j]=inf;
		}
	}


	for(i=1; i<numPosts; i++)
	{
		for(j=i-1; j>=0; j--)
		{
			for(k=j+1; k<=i; k++)
				if(check(dx[j],dy[j],dx[k],dy[k]))
				{
					dp[j][i]=min( dp[j][i],dp[k][i]+sol(dx[k],dy[k],dx[j],dy[j],dx[i],dy[i]) );
				}
		}
	}
	for(i=0; i<numPosts; i++)
	{
		for(j=i+2; j<numPosts; j++)
		{
			if(check(dx[j],dy[j],dx[i],dy[i]))
				ans=min(ans,dp[i][j]);
		}
	}
	return ans;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值