T1:比赛的时候我以为想到了正解,但是调了很长时间之后我发现了错误。正解和我的方法还是有很大差别的。
正解如下:首先贪心算出ans。然后我们从前往后枚举b的每一位放什么。
对于第i位,我们把剩下没有用过的b分成两部分——小于等于a[i]的和大于a[i]的。那么我们就可以二分两次来求b[i]了。易证b的合法性在两部分之中是有单调性的。至于判定,我们可以在二分除了mid之后再O(n)贪心做一遍剩下的数,求出第i为放mid的答案。
T2:这题我比赛时想到了正解,但是没时间打了。
二分答案,然后我们发现剩下的就是一个扫描线问题。
具体操作不好讲,还是看代码吧:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define MAXN 50010
#define pi M_PI
using namespace std;
struct point
{
double x;
double y;
ll rank;
double v;
};
point a[MAXN];
struct sor
{
double v;
ll num;
};
sor b[MAXN];
ll f[MAXN],n;
double ans,k,w;
const double L=0.000000001;
int game1(const sor &a,const sor &b)
{
if(a.v<b.v)return 1;
else return 0;
}
int game2(const point &a,const point &b)
{
if(a.v*w-b.v*w<0)return 1;
if(a.v*w-b.v*w>0)return 0;
if(a.y>b.y)return 1;
else return 0;
}
ll sum(ll x)
{
ll i=x,s=0;
while(i>=1){s+=f[i];i=i-(i&(-i));}
return s;
}
ll change(ll x)
{
ll i=x;
while(i<=n){f[i]++;i=i+(i&(-i));}
}
double find(ll num)
{
ll i,j,s;
double l,r;
l=0;r=pi;
while(l<=r)
{
k=(l+r)/2;w=tan(k);
for(i=1;i<=n;i++)a[i].v=a[i].y-w*a[i].x;
sort(a+1,a+n+1,game2);
memset(f,0,sizeof(f));
s=0;
for(i=1;i<=n;i++)
{
s=s+sum(n)-sum(a[i].rank-1);
change(a[i].rank);
}
if(s<=num-1)ans=k,l=k+L;
else r=k-L;
}
return ans;
}
int main()
{
//freopen("line.in","r",stdin);
//freopen("line.out","w",stdout);
ll i,j,w=0;
scanf("%lld",&n);
for(i=1;i<=n;i++)scanf("%lf %lf",&a[i].x,&a[i].y);
for(i=1;i<=n;i++)b[i].v=a[i].y,b[i].num=i;
sort(b+1,b+n+1,game1);
for(i=1;i<=n;i++)
{
if(b[i].v!=b[i-1].v||i==1)w++;
a[b[i].num].rank=w;
}
if((n*(n-1)/2)%2==1)printf("%.10lf",find(n*(n-1)/2/2+1));
else printf("%.10lf",(find(n*(n-1)/2/2)+find(n*(n-1)/2/2+1))/2);
}
T3:首先假设我们已经建完图了,那么设g[i]表示有多少个j满足j>i且i、j有连边,那么。这是因为在i后面与i有连边的所有点必定构成一个完全图,所以这个完全图中的点两两的颜色肯定是不同的,而i有与它们都有连边,所以i就只剩下了(n-g[i])中选择。所有i的方案乘起来就是答案了。
那么现在的问题就是求g,这里有一个简单的方法:
从前往后扫,对于每个点维护一棵线段树,然后把i的线段树合并到i后面与i最近的有连边的点,这样我们就可以在O(nlogn)的复杂度内构完图。实现时可以线段树合并,也可以set+启发式合并(set维护每一个点连向的点)。