发现好久没写题解了,补几场cf的题解。
A. Linova and Kingdom
略
B. Xenia and Colorful Gems
略
C. Kaavi and Magic Spell
略
D. Yui and Mahjong Set
吐了,辛辛苦苦推了一年依次问 1 ∼ n 1\sim n 1∼n的算法,结果交上去WA了,又分析了半天发现只有当 n > 5 n>5 n>5的时候才能保证正确,于是怒膜题解去了。
题解的询问方式是先依次问 n − 1 , n − 2 , . . . , 3 n-1,n-2,...,3 n−1,n−2,...,3,然后再依次问 1 , 2 , 1 1,2,1 1,2,1。
注意到我们问了 1 1 1两次之后,一定能得到 a 1 a_1 a1(第二次问时考虑triplet的增量为 ( a 1 + 1 2 ) \binom{a_1+1}{2} (2a1+1),这个是可以唯一还原 a 1 a_1 a1的)。同时第一次问 1 1 1时的straight增量为 a 2 ⋅ ( a 3 + 1 ) a_2\cdot (a_3+1) a2⋅(a3+1),而第二次问 1 1 1时的straight增量为 ( a 2 + 1 ) ⋅ ( a 3 + 1 ) (a_2+1)\cdot (a_3+1) (a2+1)⋅(a3+1),因此容易得到 a 2 a_2 a2和 a 3 a_3 a3。
再注意到对于 i ≥ 4 i\geq 4 i≥4,若我们已经得到了 a 1 ∼ a i − 1 a_1\sim a_{i-1} a1∼ai−1,那么容易根据问 i − 2 i-2 i−2时的straight增量得到 a i a_i ai(减掉已知部分剩下 ( a i − 1 + 1 ) ⋅ a i (a_{i-1}+1)\cdot a_i (ai−1+1)⋅ai,直接除即可)。
这样就可以在 n n n次操作后问出 a 1 ∼ a n a_1\sim a_n a1∼an了。时间复杂度为 O ( n ) \mathcal O(n) O(n)。
#include <bits/stdc++.h>
using namespace std;
int a[105],b[105];
int num[105];
int main() {
int n;
scanf("%d",&n);
int x,y;
scanf("%d%d",&x,&y);
for(int i=n-1;i>2;i--) {
printf("+ %d\n",i);
fflush(stdout);
scanf("%d%d",&a[i],&b[i]);
}
printf("+ %d\n",1);
fflush(stdout);
scanf("%d%d",&a[1],&b[1]);
printf("+ %d\n",2);
fflush(stdout);
scanf("%d%d",&a[2],&b[2]);
printf("+ %d\n",1);
fflush(stdout);
scanf("%d%d",&x,&y);
x-=a[2];
y-=b[2];
a[2]-=a[1];
b[2]-=b[1];
a[1]-=a[3];
b[1]-=b[3];
for(int i=3;i<n;i++) {
a[i]-=a[i+1];
b[i]-=b[i+1];
}
for(int i=2;i<=n+1;i++)
if ((i*(i-1)>>1)==x) num[1]=i-1;
num[3]=y-b[1]-1;
num[2]=b[1]/(num[3]+1);
assert(num[1]>=0&&num[2]>=0&&num[3]>=0);
for(int i=4;i<=n;i++) {
int s=b[i-2];
if (i>4) s-=num[i-3]*num[i-4];
s-=(num[i-3]+(i==4))*(num[i-1]+1);
num[i]=s/(num[i-1]+1)-(i<n);
assert(num[i]>=0);
}
printf("! ");
for(int i=1;i<=n;i++) printf("%d ",num[i]);
printf("\n");
fflush(stdout);
return 0;
}
/*
5
1 6
1 15
4 20
5 24
5 40
8 48
*/
E2. Chiori and Doll Picking (hard version)
这题好神啊,膜了一天题解。
显然只用考虑线性基里的元素。令线性基大小为 k k k,只用求出只使用线性基里元素的答案再乘上 2 n − k 2^{n-k}