Luogu P9165 「INOH」Round 1 - 意外
给定一个长度为 \(10^2\),值域为 \([0,998244353)\) 的整数数组 \(A\)。你需要构造一个长度不超过 \(750\),值域为 \([0,998244353)\) 的整数数组 \(B\),接下来对于每个下标 \(i\),交互库都有 \(\dfrac{1}{2}\) 的概率将 \(B_i\) 修改为 \([0,998244353)\) 内的随机整数。记修改后的数组为 \(C\),你需要通过数组 \(C\) 还原数组 \(A\)。
你需要实现函数
Encode和Decode。其中Encode传入数组 \(A\),你需要给出数组 \(B\)。Decode传入数组 \(C\),你需要给出数组 \(A\)。交互库调用Encode的次数不超过 \(3 \times 10^4\),调用Decode的次数不超过 \(10^4\)。
考虑最简单的想法,将 \(A\) 中的每个元素重复 \(7\) 次得到 \(B\),在 \(C\) 的每 \(7\) 个元素中找到出现次数 \(\geq 2\) 的元素作为 \(A\),因为我们可以认为随机生成的数两两不同。这个做法对于单个元素的错误概率已经较高了,而一共会产生的元素个数为 \(10^2 \times 10^4 = 10^6\),这个做法完全不可取。
接下来考虑优化。我们考虑对于一个元素,重复 \(k\) 次后错误率为 \(\dfrac{k+1}{2^k}\),用这种方法传递 \(t\) 个元素,期望有 \(t \times (1-\dfrac{k+1}{2^k})\) 个元素有效。
我们考虑对 \(A\) 进行类似哈希的操作。容易发现在问题下,为了还原长度为 \(100\) 的整数数组,需要 \(100\) 个有效的哈希值。而假设我们对一个哈希值重复 \(k\) 次,则期望有 \(\dfrac{750}{k} \times (1-\dfrac{k+1}{2^k})\),此时容易使其 \(\geq 100\)。接下来考虑我们进行哈希的方法,需要使得在所有哈希结果构成的集合 \(S\) 中取出任意大小 \(\geq 100\) 的子集都能还原数组,考虑将原数组看成多项式,只需要将点值作为哈希值,还原时进行插值即可。
时间复杂度不太会分析,正确率不太会分析,摆烂。
#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
const long long mod=998244353;
const int N=100,K=5,M=150,L=750;
long long inv(long long num){
num=(num%mod+mod)%mod;
long long val=mod-2,ans=1;
while(val){
if(val&1){
ans=ans*num%mod;
}
num=num*num%mod;
val>>=1;
}
return ans;
}
vector<int> Encode(vector<int> vec){
vector<int> ans;
for(int i=0;i<M;i++){
long long val=0;
for(int j=N-1;j>=0;j--){
val*=i;
val%=mod;
val+=vec[j];
val%=mod;
}
for(int j=0;j<K;j++){
ans.push_back(int(val));
}
}
return ans;
}
long long x[M+10],y[M+10],f[M+10],tmp[M+10],final[M+10],inv_num[2*M+10];
vector<int> Decode(vector<int> vec){
for(int i=0;i<M;i++){
x[i]=y[i]=f[i]=final[i]=0;
}
int tot=0;
for(int i=0;i<M;i++){
long long val=-1;
map<int,int> dict;
for(int j=0;j<K;j++){
dict[vec[i*K+j]]++;
if(dict[vec[i*K+j]]>1){
val=vec[i*K+j];
}
}
if(val!=-1){
x[tot]=i;
y[tot]=val;
tot++;
}
}
for(int i=-M;i<=M;i++){
inv_num[i+M]=inv(i);
}
f[0]=1;
for(int i=0;i<tot;i++){
for(int j=0;j<=tot;j++){
tmp[j]=0;
}
for(int j=0;j<tot;j++){
tmp[j]-=x[i]*f[j]%mod;
tmp[j]=(tmp[j]%mod+mod)%mod;
}
for(int j=1;j<=tot;j++){
tmp[j]+=f[j-1];
tmp[j]=(tmp[j]%mod+mod)%mod;
}
for(int j=0;j<=tot;j++){
f[j]=tmp[j];
}
}
for(int i=0;i<tot;i++){
long long add_val=y[i];
for(int j=0;j<tot;j++){
if(j!=i){
add_val*=inv_num[x[i]-x[j]+M];
add_val%=mod;
}
}
for(int j=0;j<=tot;j++){
tmp[j]=f[j];
}
for(int j=tot;j>=1;j--){
final[j-1]+=tmp[j]*add_val%mod;
final[j-1]%=mod;
tmp[j-1]+=tmp[j]*x[i]%mod;
tmp[j-1]%=mod;
}
}
vector<int> ans;
for(int i=0;i<N;i++){
ans.push_back(final[i]);
}
return ans;
}
然而被卡常了,所以下面放一份经过 deepseek 卡常后通过的代码。
#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
const long long mod = 998244353;
const int N = 100, K = 5, M = 150, L = 750;
// 快速幂求逆元 - 使用引用避免拷贝
inline long long inv(long long num) {
num = (num % mod + mod) % mod;
long long val = mod - 2, ans = 1;
while (val) {
if (val & 1) ans = ans * num % mod;
num = num * num % mod;
val >>= 1;
}
return ans;
}
vector<int> Encode(vector<int> vec) {
vector<int> ans;
ans.reserve(M * K); // 预分配空间
for (int i = 0; i < M; i++) {
long long val = 0;
// 使用霍纳法则计算多项式值
for (int j = N - 1; j >= 0; j--) {
val = (val * i + vec[j]) % mod;
}
for (int j = 0; j < K; j++) {
ans.push_back((int)val);
}
}
return ans;
}
vector<int> Decode(vector<int> vec) {
// 使用静态数组避免重复分配
static long long x[M + 10], y[M + 10], f[M + 10], tmp[M + 10];
static long long final[M + 10], inv_num[2 * M + 10];
int tot = 0;
// 第一步:收集有效点
for (int i = 0; i < M; i++) {
// 使用数组统计频率,避免map开销
static int freq[mod % 1000]; // 根据实际情况调整大小
// 或者使用小范围映射
int val = -1;
int same = 0;
int first = vec[i * K];
for (int j = 1; j < K; j++) {
if (vec[i * K + j] == first) same++;
}
if (same >= 1) { // K>=2时,至少有1次重复
x[tot] = i;
y[tot] = first;
tot++;
}
else {
// 检查其他值是否有重复
for (int j = 0; j < K; j++) {
for (int k = j + 1; k < K; k++) {
if (vec[i * K + j] == vec[i * K + k]) {
x[tot] = i;
y[tot] = vec[i * K + j];
tot++;
j = K; // 跳出外层循环
break;
}
}
}
}
}
// 预计算逆元表
if (inv_num[M] == 0) { // 只计算一次
for (int i = -M; i <= M; i++) {
if (i == 0) continue;
inv_num[i + M] = inv(i);
}
}
// 构造多项式 Π(x - xi)
f[0] = 1;
int poly_deg = 0;
for (int i = 0; i < tot; i++) {
// 乘以 (x - x[i])
tmp[poly_deg + 1] = f[poly_deg];
for (int j = poly_deg; j >= 1; j--) {
tmp[j] = (f[j - 1] - f[j] * x[i]) % mod;
if (tmp[j] < 0) tmp[j] += mod;
}
tmp[0] = (-f[0] * x[i]) % mod;
if (tmp[0] < 0) tmp[0] += mod;
poly_deg++;
for (int j = 0; j <= poly_deg; j++) {
f[j] = tmp[j];
}
}
// 清零final数组
for (int i = 0; i <= poly_deg; i++) final[i] = 0;
// 拉格朗日插值
for (int i = 0; i < tot; i++) {
// 计算插值系数
long long coef = y[i];
for (int j = 0; j < tot; j++) {
if (j != i) {
coef = coef * inv_num[x[i] - x[j] + M] % mod;
}
}
// 多项式除法:f(x) / (x - x[i])
long long remainder = 0;
for (int j = poly_deg; j >= 0; j--) {
long long t = (f[j] + remainder) % mod;
if (j > 0) {
final[j - 1] = (final[j - 1] + t * coef) % mod;
remainder = t * x[i] % mod;
}
}
}
vector<int> ans;
ans.reserve(N);
for (int i = 0; i < N; i++) {
ans.push_back(final[i] % mod);
}
return ans;
}

浙公网安备 33010602011771号