20200603 模拟赛 排列

8 篇文章 0 订阅
2 篇文章 0 订阅

在这里插入图片描述
在这里插入图片描述

题解:
n ! n! n!个排列可以看做 n ! n! n! n n n维点,那么序列 x x x可以被构造出来当且仅当 x x x在这些点的凸包上(是刚好在表面上而非内部。)
探索在凸包上的条件:
1. ∑ x = n ( n + 1 ) 2 \sum x = \frac {n(n+1)}2 x=2n(n+1)
2. ∑ i = 1 k y i ≥ k ( k + 1 ) 2 \sum_{i=1}^k y_i \geq \frac {k(k+1)}2 i=1kyi2k(k+1),其中 y y y是给 x x x按从小到大排序得到的数组。
这两个条件都是必要的。
第二个条件,因为前 k k k个位置的和如果 < k ( k + 1 ) 2 \lt \frac{k(k+1)}2 <2k(k+1)的话,就算选了 n n n个排列每个排列的前 k k k个都尽量小,总和也有 k ( k + 1 ) n 2 \frac {k(k+1)n}2 2k(k+1)n,不能更小,所以两边在前 k k k位的和都对不上,更别提对位相等了。
断言:满足这两个条件的序列 x x x一定可以被构造出来。(充分性存在)
构造方案如下:
每次把 x x x排好序,考虑把 x x x减去一个 p , 2 p , 3 p . . . . n p p,2p,3p....np p,2p,3p....np(就是一个 1 1 1 n n n的排列乘上 p p p)。
考虑减去后仍可以被 n − 1 n-1 n1个排列构造出来的条件:
1. ∑ x = n ( n + 1 ) 2 ( 1 − p ) \sum x = \frac {n(n+1)}2(1-p) x=2n(n+1)(1p)
2. ∑ i = 1 k y i ≥ k ( k + 1 ) 2 ( 1 − p ) \sum_{i=1}^k y_i \geq \frac {k(k+1)}2 (1-p) i=1kyi2k(k+1)(1p)
那么二分出最大的 p p p使得 x x x被减少后排序后满足上列条件。
这样的 p p p可以使得至少一个实际上 ∑ i = 1 k x i > k ( k + 1 ) 2 \sum_{i=1}^k x_i \gt \frac {k(k+1)}2 i=1kxi>2k(k+1)的位置 k k k
在减少之后满足 ∑ i = 1 k y i = k ( k + 1 ) 2 ( 1 − p ) \sum_{i=1}^k y_i = \frac {k(k+1)}2(1-p) i=1kyi=2k(k+1)(1p)
可以推推式子证明任何 ∑ i = 1 k x i = k ( k + 1 ) 2 \sum_{i=1}^k x_i =\frac {k(k+1)}2 i=1kxi=2k(k+1)的位置 k k k
在减少之后满足 ∑ i = 1 k y i = k ( k + 1 ) 2 ( 1 − p ) \sum_{i=1}^k y_i = \frac {k(k+1)}2(1-p) i=1kyi=2k(k+1)(1p)
所以减一次就会少一个实际上大于的位置。
一开始在 n n n的位置就是等于。
所以经过了 n − 1 n-1 n1次就一定会变成 1... n 1...n 1...n每个位置都是等于。
这个时候减一个 p , 2 p , 3 p . . . . n p p,2p,3p....np p,2p,3p....np就可以直接消为 0 0 0
会发现这个方案在满足初始条件的情况下会一直满足,构造到全变为 0 0 0
所以证明成立。
A C   C o d e \mathcal AC \ Code AC Code

#include<bits/stdc++.h>
#define maxn 505
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define db double
#define eps 1e-12
#define LL long long
using namespace std;

int n,c[maxn],c2[maxn];
db x[maxn],p[maxn],x2[maxn];
int y[maxn][maxn];
bool cmp(const int &u,const int &v){ return x[u] < x[v]; }
bool cmp2(const int &u,const int &v){ return x2[u] < x2[v]; }

int main(){
	
	freopen("permutation.in","r",stdin);
	freopen("permutation.out","w",stdout);
	
	scanf("%d",&n);
	rep(i,1,n)scanf("%lf",&x[i]),c[i]=i;
	db rs = 1;
	rep(i,1,n){
		sort(c+1,c+1+n,cmp);
		memcpy(c2,c,sizeof c2);
		
		db L = 0 , R = rs , mid;
		for(;(R-L) > eps;){
			mid = (L+R) * 0.5;
			rep(j,1,n) x2[c[j]] = x[c[j]] - j * mid;
			sort(c2+1,c2+1+n,cmp2);
			db sm = 0;
			bool ER = 0;
			rep(j,1,n){
				sm += x2[c2[j]];
				if(sm < (rs - mid) * j * (j+1) / 2 - eps){
					ER = 1;
					break;
				}
			}
			if(ER) R = mid - eps;
			else L = mid;
		}
		p[i] = L;
		if(i == n) p[i] = rs;
		rs -= p[i];
		rep(j,1,n) x[c[j]] -= p[i] * j;
		rep(j,1,n) y[i][c[j]] = j;
	//	rep(j,1,n) printf("%.3lf%c",x[j]," \n"[j==n]);
	}
	if(rs > 1e-6){
		puts("-1");
		return 0;
	}
	rep(i,1,n) if(fabs(x[i]) > 1e-2){
		puts("-1");
		return 0;
	}
	printf("%d\n",n);
	rep(i,1,n){
		printf("%.10lf ",p[i]);
		rep(j,1,n) printf("%d%c",y[i][j]," \n"[j==n]);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值