3.5考试总结

3.5考场总结

这次的问题还是有:

  1. #define int long long
  2. 出现了刚题现象,导致 没有看T2,T2是水题
  3. 做题着急了,应该一步步推完所有的东西后再回来看,着急了,前面推矩阵的时候出现了不该出现的错误,侧面体现出做题少 ,因为这玩意显然不能前两列长得一样,而且矩阵乘法还想了半天,这样学必然会出事

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;
}
posted @ 2022-03-06 12:02  RevolutionBP  阅读(132)  评论(0编辑  收藏  举报