3.18 UR17

打了UOJ Round 17,这么低的分居然还涨rating?!
T1:
开始读错题了,1h以后写的暴力过不了样例开始慌,手玩样例发现读错题了,然而并没有时间改,写了子任务123就跑了。
T2:
计算几何的气息,直接跳过。
T3:
概率期望?看来没得做了。手玩了菊花图的10分,打个表发现其余的跑不出来,弃掉了。
捆绑测试,有分基本上也都卡没了。

题解:
T1:
最优答案肯定是一条链。
对于任务34,我们可以直接找最小的,最多log次就会减到最小,复杂度\(O(n*log_{2}{n})\)
对于任务5,我们可以枚举一下a[i],设计状态数组f[i]表示在状态i时,变成0最少需要多少代价。
f[i]=min(f[i],f[i&a[i]]+a[i]),复杂度\(O(n*max_w)\)
满分做法是我们不枚举a[i],而是枚举一个子集S,找a[]中是否有一个数和它与起来为0.
这样先预处理a[i]补集的子集就可以了,复杂度是\(O(3^{log_{2}max_w})\)
转移方程就变成了\(f[i]=min(f[i],f[i^s]+i^s)\)
注意可能所有的数与起来不为0,在总的或起来的数异或上它就可以了。

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
#define int long long 
#define mmin(a,b) (a<b?a:b)
int n,f[601000],a[601000];
bool pd[601000];
signed main()
{
//	freopen("ex_treeand3.in","r",stdin);
	scanf("%lld",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	int all=0,zz=0;
	for(int i=1;i<=n;i++) all|=a[i];
	zz=all;
	for(int i=1;i<=n;i++) zz&=a[i];
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)
	{
		pd[all^a[i]]=1;
		a[i]^=zz;
	}
	all^=zz;
	pd[0]=1;
	for(int i=all;i>=0;i--)
	{
		if(zz&i) continue;
		for(int j=0;j<=18;j++)
		{
			if(zz&(1<<j)) continue;
			pd[i]|=pd[i|(1<<j)];
		}
	}
	memset(f,30,sizeof(f));
	f[0]=0;
	for(int i=1;i<=all;i++)
	{
		if(i&zz) continue;
		for(int j=i;j;j=(j-1)&i)
		{
			if((i^j)>f[i]) break;
			if(pd[j]) f[i]=mmin(f[i],f[i^j]+(i^j));
		}
	}
	printf("%lld",f[all]+zz*n);
	return 0;
}

T2:
对于一定在端点的情况,我们设状态数组f[i][j],表示一个点为i,另一个点为j时是否能够到达(stx,sty);
然后二分答案,记忆化一下,复杂度\(O(n^2*log_{2}n)\)
满分做法:
仍然是二分答案。
设状态数组f[i][j]表示一个点在第i个节点,一个点在第j条边上是否合法。
i和j的最小距离必须小于等于二分的答案v。
由于把一条直线摆直时,另一条直线一定单调,所以取最小距离就可以了。
f[i][j]可以有f[k][j]转移过来,k为j的某个端点,j为与i相连的直线;或f[k][j],k与i相连。
仍然是bfs一下,复杂度\(O(n^2*log_{2}n)\)

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
using namespace std;
#define fang(x) ((x)*(x))
#define pai pair<int,int>
struct point{
	double x,y;
	int id;
}q[1010];
point operator + (const point &s1,const point &s2) {return (point){s1.x+s2.x,s1.y+s2.y};}
point operator - (const point &s1,const point &s2) {return (point){s1.x-s2.x,s1.y-s2.y};}
double operator * (const point &s1,const point &s2) {return s1.x*s2.y-s1.y*s2.x;}
struct line{
	point a,b;
}l[1010];
int n,f[1010][1010],du[1010],stx,sty;
double eps=1e-7;
void bfs(double);
vector<int> ve[1010];
double getdisxian(point,line);
double getdisdian(point,point);
point getpoint(line,line);
bool check(double);
signed main()
{
//	freopen("in.txt","r",stdin);
	scanf("%d%d%d",&n,&stx,&sty);
	for(int i=1;i<=n;i++) scanf("%lf%lf",&q[i].x,&q[i].y),q[i].id=i;
	for(int i=1;i<n;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		l[i].a=q[x]; l[i].b=q[y];
		if(q[x].x>q[y].x) swap(l[i].a,l[i].b);
		du[x]++; du[y]++;
		ve[x].push_back(i);
		ve[y].push_back(i);
	}
	double le=0,ri=2e6;
	while(ri-le>eps)
	{
		double mid=(le+ri)*0.5;
		bfs(mid);
		if(check(mid)==1) ri=mid;
		else le=mid;
	}
	printf("%.7f",le);
	return 0;
}
void bfs(double xian)
{
	memset(f,0,sizeof(f));
	if(getdisdian(q[stx],q[sty])>xian) return ;
	queue<pai> que;
//	cout<<getdisxian(q[8],l[5])<<endl;
	for(int i=1;i<=n;i++)
	{
		if(du[i]!=1) continue;
		for(int j=1;j<=n;j++)
		{
			if(du[j]==1)
			{
				if(getdisdian(q[i],q[j])>xian)
				{
					f[i][ve[j][0]]=-1;
					continue;
				}
				if(getdisxian(q[i],l[ve[j][0]])>xian)
				{
					f[i][ve[j][0]]=-1;
					continue;
				}
				f[i][ve[j][0]]=1;
				que.push((pai){i,ve[j][0]});
			}
		}
	}
	int sz;
	while(que.empty()==0)
	{
		pai x=que.front(); que.pop();
		sz=ve[x.first].size();
		for(int i=0;i<sz;i++)
		{
			int xi=ve[x.first][i];
			if(f[l[x.second].a.id][xi]==0)
			{
				if(getdisxian(l[x.second].a,l[xi])<xian)
				{
					f[l[x.second].a.id][xi]=1;
					que.push((pai){l[x.second].a.id,xi});
				}
				else
				{
					f[l[x.second].a.id][xi]=0;
				}
			}
			if(f[l[x.second].b.id][xi]==0)
			{
				if(getdisxian(l[x.second].b,l[xi])<xian)
				{
					f[l[x.second].b.id][xi]=1;
					que.push((pai){l[x.second].b.id,xi});
				}
				else
				{
					f[l[x.second].b.id][xi]=0;
				}
			}
		}
	}
}
double getdisdian(point a,point b)
{
	return sqrt(fang(a.x-b.x)+fang(a.y-b.y));
}
double getdisxian(point a,line b)
{
	point c;
	if(fabs(b.a.y-b.b.y)<eps) c.x=a.x,c.y=a.y+1;
	else
	{
		double k;
		if(fabs(b.a.x-b.b.x)<eps) k=0;
		else k=(b.a.y-b.b.y)/(b.a.x-b.b.x),k=-1.0/k;
		c.x=a.x+1; c.y=a.y+k;
	}
	point d=getpoint((line){a,c},b);
	if(d.x<=b.b.x&&d.x>=b.a.x) return getdisdian(a,d);
	else
	{
		if(d.x<b.a.x) return getdisdian(a,b.a);
		else return getdisdian(a,b.b);
	}
}
point getpoint(line a,line b)
{
	point p;
	double dot1=(b.a-a.a)*(b.b-a.a),dot2=(b.b-a.b)*(b.a-a.b);
	p.x=(a.a.x*dot2+a.b.x*dot1)/(dot1+dot2);
	p.y=(a.a.y*dot2+a.b.y*dot1)/(dot1+dot2);
	return p;
}
bool check(double xian)
{
	if(getdisdian(q[stx],q[sty])>xian) return false;
	for(int i=0;i<ve[stx].size();i++)
		if(f[sty][ve[stx][i]]==1)
			return true;
	for(int i=0;i<ve[sty].size();i++)
		if(f[stx][ve[sty][i]]==1)
			return true;
	return false;
}

T3:
题解和代码都鸽了……

posted @ 2018-03-24 21:44  hzoi_wangxh  阅读(286)  评论(0编辑  收藏  举报