联考20200603 T2 排列

题目:


分析:
把排列看做一个\(n\)维上的点,\(n!\)个排列会在\(n\)维空间下构成一个凸包
如果输入的排列表示的点是在凸包上,那么就一定有解
什么玄学玩意?
其实原序列为\(X\),假设\(X\)从小到大排序后的序列为\(Y\),如果有解,当且仅当满足下面两个条件:

\(\sum_{i=1}^{n}Y_i=\frac{n(n+1)}{2}\)
\(\sum_{i=1}^{k}Y_i\geq \frac{k(k+1)}{2}\)

其中\(k\)\(1\)\(n\)的整数
冷静分析一下:
第一个式子由于\(a_i=\sum_{j=1}^{m}p_{j}b_{ij}\),求和即可证明
转化到凸包上,每一个点到原点的曼哈顿距离都是\(\frac{n(n+1)}{2}\),都可以用\(n!\)个排列形成的向量乘以模长\(p\)表示
第二个式子需要我们重新理解一下整个过程
我们可以将整个过程理解为,下标小的数需要下标大的数匀一些值过来,我们要求的便是匀过来值的占比
而如果一个前缀他自身需要的值比\(\frac{k(k+1)}{2}\)小,后面的数匀过来只会让它变大,无解
转化到凸包上,不合法的点会在凸包某一个\(n-1\)维的面上,但是不会再凸包上,无法表示

我们先把序列排序形成\(Y\),用\(1...n\)的序列作为向量\(a\)乘以尽量长的\(p\)来表示
假设我们求出了一个\(p\),向量\(pa\)从原点出发,终点假设为\(A\)
凸包上,每一个点到\(A\)的曼哈顿距离都是\(\frac{(1-p)n(n+1)}{2}\),这个不难看出
那么原序列向量\(Y\)减去向量\(pa\),然后从小到大排序得到的\(Y'\)依然要满足:

\(\sum_{i=1}^{n}Y'_i=\frac{(1-p)n(n+1)}{2}\)
\(\sum_{i=1}^{k}Y'_i\geq \frac{(1-p)k(k+1)}{2}\)

证明与上面同理
我们可以二分出最大的\(p\),递归求解即可,由于每一次一定会有某一维达到瓶颈,我们最多只需要做\(n\)

神仙题,可能讲不大清楚,先口胡这么长一段(
看代码可能能帮助理解

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<iostream>
#include<map>
#include<bitset>
#include<string>

#define maxn 40005
#define INF 1<<30
#define eps 1e-13

using namespace std;

inline long long getint()
{
    long long num=0,flag=1;char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
    return num*flag;
}

int n;
struct node{
	long double a;
	int id;
}A[maxn];
inline bool cmp(node x,node y){return x.a<y.a;}
long double B[maxn];
int sum;
int ans[maxn];

int main()
{
	n=getint();
	for(int i=1;i<=n;i++)
	{
		int x=getint();
		sum+=x,A[i].a=x;A[i].id=i;
	}
	if(sum!=n*(n+1)/2){puts("-1");return 0;}
	sort(A+1,A+n+1,cmp);
	long double num=0,now=1;
	for(int i=1;i<=n;i++)
	{
		num+=A[i].a;
		if(num<i*(i+1)/2){puts("-1");return 0;}
	}
	printf("%d\n",n);
	for(int t=1;t<=n;t++)
	{
		sort(A+1,A+n+1,cmp);
		long double l=0,r=now;
		while(r-l>eps)
		{
			long double mid=(l+r)/2;bool flg=0;
			for(int i=1;i<=n;i++)
			{
				B[i]=A[i].a-mid*i;
				if(B[i]<-eps){flg=1;break;}
			}
			sort(B+1,B+n+1);
			long double num=0;
			for(int i=1;i<=n;i++)
			{
				num+=B[i];
				if(num+eps<(now-mid)*i*(i+1)/2){flg=1;break;}
			}
			if(flg)r=mid;
			else l=mid;
		}
		printf("%.12lf",(double)l);now-=l;
		for(int i=1;i<=n;i++)ans[A[i].id]=i,A[i].a-=l*i;
		for(int i=1;i<=n;i++)printf(" %d",ans[i]);
		printf("\n");
	}
}

posted @ 2020-06-03 22:10  Izayoi_Doyo  阅读(249)  评论(0编辑  收藏  举报