3.5考试总结
3.5考场总结
这次的问题还是有:
#define int long long
- 出现了刚题现象,导致 没有看T2,T2是水题
- 做题着急了,应该一步步推完所有的东西后再回来看,着急了,前面推矩阵的时候出现了不该出现的错误,侧面体现出做题少 ,因为这玩意显然不能前两列长得一样,而且矩阵乘法还想了半天,这样学必然会出事
T1
在乏味的黎曼几何课上,你为了打发时间,玩起了硬币。 你在桌子上画了三个圈,这三个圈顺时针编号为 1, 2, 3。首先,你将 N 枚硬币全部垒放在 1 号圈里 面。这些硬币从下到上面值依次为 N, N − 1, · · · , 1。你每次将某一个圈里面值最小的硬币移动到顺时针 相邻的下一个圈里 (即 1 → 2, 2 → 3, 3 → 1)。并且,你需要保证,目标圈里的所有硬币都比你移动的这 个硬币的面值大。这样的一次操作称为“一步”。 为了不让老师察觉到你的行为,你决定尽快将这些硬币全部移动到某一个格子。由于你还没有想好 移动到哪个格子可以更快完成,你决定先计算出移动到 2 号格子和 3 号格子格子需要的最少步数。 (实际上,根据数据范围,你在移动总重量为 \(10^6\) 吨的硬币)
经过思考:
\(a_i = 2b_{i-1}+1=2a_{i-1}+2a_{i-2}+3\)
\(b_i=a_i+a_{i-1}+1=2b_{i-1}+a_{i-1}+2=2b_{i-1}+2b_{i-2}+3\)
矩阵快速幂即可
这里有一个技巧光速幂,可以预处理后 \(O(1)\) 输出答案,由于底数 \(B\) 数组是不变的,因此 ,我们可以把指数 \(k\) 拆成 \(ax+b\) 的形式,然后分别求解 \(B^{ax}\times B^b\) 的值即可
其实很好想明白,我们首先规定一个数,实验证明最优为 \(\sqrt k\)。然后把 \(1\le s \le \sqrt k\) 的每一个 \(B^s\) 的矩阵算出来,存到一个数组 \(c\) 里,然后再把每一个大于 \(\sqrt k\) 的用倍增的方法求出来(类似于每次求出最高位),扔到另一个数组 \(d\)
对于每一个数,我们只需要输出 \(d[p/\sqrt k] \times c[p\% \sqrt k]\) 即可
代码大概如下
inline void initB() {
B.a[0][0] = 2, B.a[0][1] = 1, B.a[0][2] = 0;
B.a[1][0] = 2, B.a[1][1] = 0, B.a[1][2] = 0;
B.a[2][0] = 3, B.a[2][1] = 0, B.a[2][2] = 1;//预处理出B
Matrix I;
I.build();
pw[0][0] = pw[1][0] = I;
rep (i, 1, 1e6)
pw[0][i] = pw[0][i - 1] * B;//1 <= s <= sqrt(k)
rep (i, 1, 1e6)
pw[1][i] = pw[1][i - 1] * pw[0][1000000]; //s > sqrt(k)
}
inline Matrix gsm(int k) {
return pw[1][k / 1000000] * pw[0][k % 1000000]; //直接 O(1) 求解即可
}
现在,我们有两个一模一样的环,每个环上有 n 个数字。如果将环从中间任意一个地方剪开,则每一个环都是一个长度为 n 的序列 \(a_{0···n−1}\)。初始,这两个环是重合的,对应位置的数字完全相同。
现在,我们将其中一个环旋转一个角度,使得这个环与另一个环对应的数字恰好岔开了 \(k\) 个。也就是,现在这个环的第 \(i\) 个数字与另一个环的第 \((i+k)\bmod n\) 对应。现在,我们求这两个环对应位置的数相乘,然后把乘积加起来,得到 \(S\)。
你的任务是将 \(a\) 重新排列,使得 \(S\) 尽可能大。输出这个最大的 \(S\)。
\(1\le n \le 5000\)
大型结论题,需要猜好多结论
第一步转换题意:
将 n 个给定的数字填入 \(\gcd(n, k)\) 个大小均为 \(n/ \gcd(n, k)\) 的环中,使得环上相邻两个数的积的和最大。
结论一:经过严格的打表验证,我们的每个环假如按升序排列的话,应该是在最中间放一个,左边一个,右边一个的时候这个环上值最大
大概就是上面这样
这个结论我试试能不能证明
首先我们证明数列是先递减后递增的
设:\(a\le b\le c\le d\)
对于 \(abcd\) 来说,我们的值为\(ab+bc+cd+ad\)
对于 \(acbd\) 来说,我们的值为\(ac+cb+bd+ad\)
上减下得 \(a(b-c)+d(c-b) = (a-d)*(b-c)\)
由正负性可知 \(\le0\),故若出现递增的一段相邻的两个非端点数并且 \(x<y\),则交换后答案必定不变或变优
所以我们据此我们可以证明单峰性,即数列是先递减后递增的
然后就要证明我们这个左右乱跑的正确性了
我们设 \(a\le b\le c\le d\le e\le f\)
然后我们令数列为我们说的最优策略
\(ecabdf\)
那么我们考虑如果我们变成了 \(edabcf\) 的话,结果会怎么样
\(ec+ac+ab+bd+df+ef\)
\(ed+ad+ab+bc+cf+ef\)
上下相减可得 \(ac+bd+df-ad-bc-cf =d(b+f)-c(b+f)+a(c-d)=(d-c)*(b+f-a)\)
必然是 \(\ge0\) 的因此就证明了我们这个猜想的正确性
那我们不妨让环断开成的链两段分别是最大值和次大值
然后每一次往里面填一个次大值
下面我们去考虑其他的性质。。。
每个环上出现的数字是原序列中连续的一段
如果一个环是 \(a_{1,2,3,5}\) 另一个是 \(a_{4,6,7,8}\)
我们考虑原来的和现在的 \(4\) 和 \(5\) 的贡献:
\(a_5*(a_2+a_3)+a_4*(a_7+a_6)\)
\(a_4*(a_2+a_3)+a_5*(a_6+a_7)\)
这两个的贡献值来说先作差
\((a_5-a_4)*(a_2+a_3)+(a_4-a_5)*(a_7+a_6)=(a_5 - a_4)(a_2+a_3-a_6-a_7)\)
\(a_5-a_4 \ge 0\) \(a2+a_3-a_6-a_7\le 0\)
因此这个差值 \(\le 0\)
所以说我们从上面得到换成下面的以后值会变大或不变,不会变劣
那么我们就等价于是在 \(a\) 数组里每次在去找 \(n/\gcd(n,k)\) 长度的连续段,每次按照上面的左右方法,就是可以求得答案
打表程序:
read(n);
rep(i, 1, n) read(a[i]);
do {
int ans = 0;
rep (i, 1, n - 1) ans += a[i] * a[i + 1];
ans += a[n] * a[1];
maxn = max(ans, maxn);
} while(next_permutation(a + 1, a + 1 + n));
sort (a + 1, a + 1 + n);
do {
int ans = 0;
rep (i, 1, n - 1) ans += a[i] * a[i + 1];
ans += a[n] * a[1];
if (ans == maxn){
rep (i, 1, n) cout << a[i] << " ";
cout << endl;
}
} while(next_permutation(a + 1, a + 1 + n));
write(maxn, '\n');
AC代码:
ll n, m, k, a[N], T;
ll ans;
inline ll gcd(ll x, ll y) {
if(y == 0) return x;
return x % y == 0 ? x : gcd(y, x % y);
}
inline ll solve(int l, int r) {
ll res = 0, head = a[l], tail = a[l];
rep (i, l + 1, r) {
if (i & 1) res += head * a[i], head = a[i];
else res += tail * a[i], tail = a[i];
}
res += head * tail;
return res;
}
inline ll calc(ll k) {
int len = n / __gcd(n, k);
ll res = 0;
for (int i = 1, j; i <= n; i = j + 1)
j = i + len - 1, res += solve(i, j);
return res;
}
int main() {
read(n);
for (int i = 1; i <= n; i++)
read(a[i]);
sort(a + 1, a + n + 1);
read(T);
while (T--){
read(m);
write(calc(m), '\n');
}
return 0;
}
// write: RevolutionBP
T3
由于你在黎曼几何课堂上玩弄硬币,在代数几何课堂上玩剪纸,到考试的时候你发现你看不懂试卷了。
于是,你打算预测一下其他同学的排名来打发考试时间。
在考试结束之前,所有学生的成绩都是不确定的。但是由于每个学生的水平存在差距,成绩并不会完全随机,你还是有办法预测一个学生的排名的。
学生ii 的总分将会是区间 \([Li,Ri]\) 内均匀分布的一个实数。
现在,请你求出每个同学的期望排名。排名的定义是考试结束后,分数大于等于自己分数的人的个数。即,最高分的排名为 \(1\),以此类推。如果你的输出与标准答案的相对误差不超过 \(10^{−6}\) 即算正确。
可以直接求解的期望题,这里只给出 \(O(n^2)\) 写法
int n;
struct node{
int l, r, id;
bool operator < (const node &b) const{
return l != b.l ? l < b.l : r < b.r;
}
}a[N];
double p[N][N], ans[N];
signed main(){
read(n);
rep (i, 1, n) read(a[i].l, a[i].r), a[i].id = i;
rep (i, 1, n) ans[i] = 1;
sort(a + 1, a + 1 + n);
rep (i, 1, n)
rep (j, i + 1, n) { // p[i][j]: p[i win j]
int l1 = a[i].l, l2 = a[j].l, r1 = a[i].r, r2 = a[j].r;
double b = r1 - l2, c = abs(r2 - r1), len1 = r1 - l1, len2 = r2 - l2;
if(l1 <= l2 && r1 <= r2) p[i][j] = (((b / len1) * (b / len2) / 2));
if(r1 < l2) p[i][j] = 0;
if(l1 <= l2 && r1 > r2) p[i][j] = ((c / len1) + ((len2 / len1) / 2));
p[j][i] = 1 - p[i][j];
}//直接算长度是怎么被覆盖的即可 S/len1*S/len2/2 对于包含的重新处理一下即可
rep (i, 1, n)
rep (j, 1, n) ans[a[i].id] += p[j][i];
rep (i, 1, n) printf("%.12lf\n", ans[i]);
return 0;
}