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:
题解和代码都鸽了……