「杂题记录」出题人
给出长度为 \(n\) 的序列 \(a_i\) ,需要构造一个序列 \(b_i\) ,使得 \(\forall i, \exists x,y \in [1, n],s.t.b_x + b_y = a_i\) 要求输出序列 \(b_i\) 和方案(\(a_i\) 由哪两个 \(b_j\) 构成) 。无解输出 \(-1\). d3A
\(n \le 30\)
分析
一道搜索模板题。
考虑如果能够构造出一个 \(a_i\) 的一个子序列 \(a'\) 的对应长度的序列 \(b'\) 那么每次增加一个元素 \(a_k\) 都可以直接构造 \(b_k\)。
如果 \(a_i\) 存在偶数,可以直接构造 \(b_i = \frac{a_i}{2}\) 。其他的元素直接构造即可。
如果 \(a_i\) 全部为奇数,考虑将 \(b_i\) 中的元素看成点,\(a_i\) 看成边,这样就有 \(n\) 个点 \(n\) 条边,一定存在一个环,只要找到这个环,剩下的元素就可以直接构造了
设:环上一个元素 \(b_k\),那么对于任意一个环外的元素 \(i\) ,可以直接构造成 \(b_i = a_i - b_k\)。
不妨设 环的大小为 \(k\), \(a_i = b_i + b_{i + 1}\),\(a_k = b_k + b_1\) 。则:\(\sum_{i=1}^{k}a_i = 2 \sum_{i = 1}^{k}b_i\),易知: \(2 | k\)。
可以直接拆成 \(a_1 + a_3 + \cdots+a_{k-1} = \sum_{i = 1}^{k}\limits{b_i}=a_2+a_4+\cdots a_k\)
传 统 艺 能 。直接搜索每个点 \(a_i\) 是属于上式中的左边、右边 或者 不属于环上的点。要求满足左边的点权和等于右边的点权和。复杂度 \(\mathcal{O}(3^n)\).
考虑把 \(a_i\) 拆成两段,分别搜索,记录最终方案,然后线性合并。即:折半搜索。
复杂度 \(\mathcal{O}(3^{n/2})\) 。
赛后因为太菜不会线性合并,带了一个 \(\log\) 卡成了 30 体验很好。后来写 hash 挂链优化掉了一个 \(\log\) ,内存占用 \(2\operatorname{GiB}\),然后人就没了。至今任然 30。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <climits>
#include <map>
#include <vector>
#define GG() return (void)puts("No")
using namespace std;
#define LL long long
#define int long long
const int _ = 1e2 + 5;
int n, A[_];
namespace subtask0{void work(){
int target = 0;
for(int i = 1; i <= n; i++) if(A[i] % 2) ; else { target = i; break; }
cout << ("Yes") << endl;
for(int i = 1; i <= n; i++){
if(i == target) {
cout << (A[i] / 2) << " ";
} else {
cout << (A[i] - (A[target] / 2)) << " ";
}
} cout << endl;
for(int i = 1; i <= n; i++){
if(i == target) {
cout << target << " " << target << endl;
} else {
cout << target << " " << i << endl;
}
}
}}
namespace subtask1{
struct status_t{
LL S;
int A0; // the sum of.
int A1; // the sum of.
int sum0; // the number of.
int sum1; // the number of.
status_t() { sum0 = sum1 = S = A0 = A1 = 0; }
} ;
vector<status_t> reta, retb;
void d( int now, status_t st, const int & ed, vector<status_t> & ret){
if(now > ed) {
ret.push_back(st);
return ;
}
status_t ext;
ext = st; ext.S *= 3; ext.S += 0; ext.A0 += A[now]; ext.sum0 ++;
d(now + 1, ext, ed, ret);
ext = st; ext.S *= 3; ext.S += 1; ext.A1 += A[now]; ext.sum1 ++;
d(now + 1, ext, ed, ret);
ext = st; ext.S *= 3; ext.S += 2;
d(now + 1, ext, ed, ret);
}
map<pair<int, int>, status_t> M;
#define none (-20031006)
int mid;
void output(status_t A, status_t B){
vector<int> Node0, Node1, nodes;
for(int i = 0, id = mid, tmp = A.S; i < mid; i++, id--, tmp /= 3) { if(tmp % 3 == 0) Node0.push_back(id); if(tmp % 3 == 1) Node1.push_back(id); }
for(int i = 0, id = n , tmp = B.S; i < (n - mid); i++, id--, tmp /= 3) { if(tmp % 3 == 0) Node0.push_back(id); if(tmp % 3 == 1) Node1.push_back(id); }
for(int i = 0; i < (int)Node0.size(); i++) nodes.push_back(Node0[i]), nodes.push_back(Node1[i]);
static int Ans[_]; for(int i = 1; i <= n; i++) Ans[i] = none;
static pair<int, int> Out[_];
Ans[nodes[0]] = 0;
for(int i = 0; i < (int)nodes.size(); i++){
Ans[nodes[(i + 1) % nodes.size()]] = ::A[nodes[i]] - Ans[nodes[i]];
Out[nodes[i]] = make_pair(nodes[i], nodes[(i + 1) % nodes.size()]);
}
static vector<int> ext;
for(int i = 1; i <= n; i++){ if(Ans[i] != none) continue;
Ans[i] = ::A[i];
Out[i] = make_pair(nodes[0], i);
}
cout << ("Yes") << endl;
for(int i = 1; i <= n; i++) cout << Ans[i] << " "; cout << endl;
for(int i = 1; i <= n; i++) cout << Out[i].first << " " << Out[i].second << endl;
}
void work(){
mid = (n >> 1);
reta.clear(); retb.clear();
d(1, status_t(), mid, reta);
d(mid + 1, status_t(), n, retb);
if(reta.size() == 0 || retb.size() == 0) GG();
for(int i = 0; i < (int)reta.size(); i++) { pair<int, int> Key; Key = make_pair(reta[i].sum0 - reta[i].sum1, reta[i].A0 - reta[i].A1); M[Key] = reta[i]; }
for(int i = 0; i < (int)retb.size(); i++) {
pair<int, int> Key; Key = make_pair(retb[i].sum1 - retb[i].sum0, retb[i].A1 - retb[i].A0);
if(M.count(Key)) ; else continue;
status_t A = M[Key], B = retb[i];
if(A.sum0 + A.sum1 == 0 || B.sum0 + B.sum1 == 0) continue;
if((A.sum0 + B.sum0 + A.sum1 + B.sum1) % 2) ;
else return output(A, B);
}
GG();
} }
signed main(){
// freopen(".in", "r", stdin);
ios::sync_with_stdio(false);
cin >> n; for(int i = 1; i <= n; i++) cin >> A[i];
int pass = false;
for(int i = 1; i <= n; i++) if(A[i] % 2) ; else { pass = true; break; }
if(pass) subtask0::work();
else subtask1::work();
return 0;
}