G
题目大意:
- 给定一个n,求出只包含1~n的一种排列
- 这个排列满足max(最长上升子序列长度,最长下降子序列长度)最小
解法:
- 构造几组数据后可以发现,如果n为8的时候,构造成5678 1234这种序列,可以把序列分成2组,并且每组的上升序列不会延申到其他组中,而每个组之间产生的为下降序列
- 所以对n进行分组,假设分成k组,每组x个的话,这个排列的值就是max(k, x),而k * x >= n,那么想要值最小,就是k和x尽量接近,那么可知
k = x = ⌈ n ⌉ k=x=\left \lceil \sqrt{n} \right \rceil k=x=⌈n⌉
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
int t;
cin >> t;
while(t --)
{
int n;
cin >> n;
int x = sqrt(n);
if(x * x < n) x += 1;
for(int i = 1;i <= x;i ++)
{
for(int j = n - x + 1;j <= n;j ++) cout << j << " ";
n -= x;
if(n < x) break;
}
for(int i = 1;i <= n;i ++) cout << i << " ";
cout << endl;
}
return 0;
}
J
题目大意:
- 给定一个n个元素的数组
- 可以把数组中的 a i a_{i} ai变成 b i b_{i} bi
- 变换之后的花费是
c o s t = ∑ i = 1 n ( a i − b i ) 2 cost=\sum_{i=1}^{n} (a_{i}-b_{i})^2 cost=i=1∑n(ai−bi)2 - 变换之后b数组需要满足是等差数列,求最小花费
解法:
- 设
b
1
b_{1}
b1以及公差
d
d
d为变量,那么对于
c
o
s
t
cost
cost求法可以得到以下式子:
c o s t = ∑ i = 1 n [ a i − b 1 − ( i − 1 ) × d ] 2 cost = \sum_{i=1}^{n}[a_{i}-b_{1}-(i-1)\times d]^2 cost=i=1∑n[ai−b1−(i−1)×d]2 - 可知cost函数是一个二元均方误差函数,所以这个函数是一个凹函数,那么求极小值解就可以利用三分套三分求,在三分
d
d
d之后,d就变成了常量,然后对上面的式子进行求导:
∑ i = 1 n − 2 × ( a i − b 1 − ( i − 1 ) × d ) \sum_{i=1}^{n}-2\times (a_{i}-b_{1}-(i-1)\times d) i=1∑n−2×(ai−b1−(i−1)×d)
⇒ − 2 ∑ i = 1 n a i + 2 n b 1 + n ( n − 1 ) d \Rightarrow -2\sum_{i=1}^{n}a_{i}+2nb_{1}+n(n - 1)d ⇒−2i=1∑nai+2nb1+n(n−1)d
令这个式子等于0,求出来的 b 1 b_{1} b1值就是 c o s t cost cost在暂定的 d d d之下的极值,那么就不需要三分套三分了,只需要三分 d d d
− 2 ∑ i = 1 n a i + 2 n b 1 + n ( n − 1 ) d = 0 -2\sum_{i=1}^{n}a_{i}+2nb_{1}+n(n - 1)d=0 −2i=1∑nai+2nb1+n(n−1)d=0
b 1 = ∑ i = 1 n a i n − ( n − 1 ) d 2 b_{1}=\frac{\sum_{i=1}^{n}a_{i}}{n} -\frac{(n-1)d}{2} b1=n∑i=1nai−2(n−1)d
#include <iostream>
#include <cstdio>
#include <cctype>
using namespace std;
const int N = 1e5 + 10;
long double a[N], sum;
int n;
namespace GTI
{
char gc(void)
{
const int S = 1 << 16;
static char buf[S], *s = buf, *t = buf;
if (s == t) t = buf + fread(s = buf, 1, S, stdin);
if (s == t) return EOF;
return *s++;
}
int gti(void)
{
int a = 0, b = 1, c = gc();
for (; !isdigit(c); c = gc()) b ^= (c == '-');
for (; isdigit(c); c = gc()) a = a * 10 + c - '0';
return b ? a : -a;
}
}
using GTI::gti;
long double get(long double d)
{
return sum / n - (n - 1) / 2.0 * d;
}
long double f(long double d)
{
long double b1 = get(d);
long double res = 0;
for(int i = 1;i <= n;i ++)
res += ((a[i] - b1 - (i - 1) * d) * (a[i] - b1 - (i - 1) * d));
return res;
}
void solve()
{
n = gti();
sum = 0;
for(int i = 1;i <= n;i ++)
{
a[i] = gti();
sum += a[i];
}
long double l = -1e9, r = 1e9;
while(r - l > 1e-10)
{
long double m1 = l + (r - l) / 3;
long double m2 = r - (r - l) / 3;
if(f(m1) <= f(m2)) r = m2;
else l = m1;
}
printf("%.9lf\n", (double)f(r));
}
signed main()
{
int t;
t = gti();
while(t --) solve();
return 0;
}
K
题目大意:
- 给定一个具有n长度的括号序列s,并且给定一个整数m
- 求解满足括号序列s是长度为m的合法序列S的子串的所有长度为m的合法括号序列S的数量
解法:
-
设 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示合法序列S的前i个位置包含括号序列s的前j个并且左括号数量比右括号数量大k个的合法括号序列数量
-
如果s第j个字符是左括号,那么S的i个位置有2种选择
选择放 s j s_{j} sj
f [ i − 1 ] [ j − 1 ] [ k − 1 ] f[i-1][j-1][k -1] f[i−1][j−1][k−1]
前提是 k − 1 > = 0 k-1>=0 k−1>=0
不放 s j s_{j} sj
f [ i − 1 ] [ j ] [ k + 1 ] f[i-1][j][k+1] f[i−1][j][k+1]
前提是 k + 1 < = m k+1<=m k+1<=m
对于右括号的以此类推 -
特殊情况
f [ 0 ] [ 0 ] [ 0 ] = 1 f[0][0][0]=1 f[0][0][0]=1
f [ i ] [ 0 ] [ k ] = 满足 i 和 k 的合法括号序列数量 , ( 1 ≤ i ≤ m , 0 ≤ k ≤ i ) f[i][0][k]=满足i和k的合法括号序列数量,(1\le i\le m,0 \le k\le i) f[i][0][k]=满足i和k的合法括号序列数量,(1≤i≤m,0≤k≤i)
#include <iostream>
#include <cstring>
using namespace std;
const int N = 210, mod = 1e9 + 7;
int f[N][N][N], n, m;
char s[N];
void init()
{
f[0][0][0] = 1;
for(int i = 1;i <= m;i ++)
for(int j = 0;j <= i;j ++)
f[i][0][j] = (f[i][0][j] + (j - 1 < 0 ? 0:f[i - 1][0][j - 1]) + f[i - 1][0][j + 1]) % mod;
}
void solve()
{
memset(f, 0, sizeof f);
cin >> n >> m >> (s + 1);
init();
for(int i = 1;i <= m;i ++)
for(int j = 1;j <= min(i, n);j ++)
for(int k = 0;k <= i;k ++)
{
if(s[j] == '(')
{
if(k - 1 >= 0) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 1][k - 1]) % mod;
if(k + 1 <= m) f[i][j][k] = (f[i][j][k] + f[i - 1][j][k + 1]) % mod;
}
else
{
if(k + 1 <= m) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 1][k + 1]) % mod;
if(k - 1 >= 0) f[i][j][k] = (f[i][j][k] + f[i - 1][j][k - 1]) % mod;
}
}
cout << f[m][n][0] << endl;
}
int main()
{
int t;
cin >> t;
while(t --) solve();
return 0;
}