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\)

你需要实现函数 EncodeDecode。其中 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;
}
posted @ 2025-12-10 23:47  Oken喵~  阅读(6)  评论(0)    收藏  举报