总结:
这把B都错题了一直Wa,然后队友告诉我说F貌似可做,写了半个小时F发现题目读假了,于是四题下班。
E - Guess the Sum
- 题目大意:
- 给定一个隐藏的、长度为N的数组,下标从0开始,题目给定L,R,要你用最少的询问次数求出\(\sum_{i = L}^{R}a_{i}\)。
- 对于每次询问,可以选择一个 i 和 j ,然后询${\textstyle\sum_{l}^{r}}ai,l = 2 ^ i * i,r = 2 ^ i * (j + 1) $ 。
- 思路分析:
- 一开始我想的是从起点开始,对于每个点枚举最大能够整除的$2^{i} $,然后直接能跳就跳,然后我Wa了,不知道为啥,当时做的时候,没想过还能够把当前的左端点变小的。然后就没思路了,去找别人的题解了。
- 假设长度为3,现在我要问 \([1, 7]\), 如果以我之前的思路,那答案会这么变化 $(1, 2), (2, 3), (4, 7) $, 这样问了3次,但能不能更优呢?
- 其实可以先问 \([0, 8)\),再问\([0, 1)\),然后把0给减掉就可以了,这就有点奇怪了,既然能往回走,就很难去想个策略去解决这个问题。题目最多只有\(2 ^ {N}\)个点,所以我们可以尝试去搜索,整张图只有\(N\cdot 2^{N}\)条边,所以建图然后从起点广搜就可以了。然后对于每个点记录前驱就可以找到整个路径了。然后建图的时候对于每个点只能走\(2 ^ {i}\),所以直接枚举次数,像 $ 2 ^ {1} - 2 ^ {2} - 2 ^ {3}$这么连边就可以了.
3.代码:
void solve() {
int N, L, R;cin >> N >> L >> R;
int up = 1 << N;
vvi g(up + 2, vi());
for(int i = 0; i <= N; i++) {
for(int l = 0; l < up; l += (1 << i)) {
int r = l + (1 << i);
g[l].push_back(r);
g[r].push_back(l);
}
}
queue<int> q;
vi pre(up + 1, -1);
vpi query;
q.push(L);
pre[L] = 0;
while(!q.empty()) {
int t = q.front();
q.pop();
if(t == R + 1) break;
for(auto v : g[t]) {
if(pre[v] != -1) continue;
pre[v] = t;
q.push(v);
}
}
for(int i = R + 1; i != L; i = pre[i]) {
query.push_back({pre[i], i});
}
reverse(query.begin(), query.end());
int sum = 0;
for(auto [l, r] : query) {
int flag = l > r ? -1 : 1;
if(flag == -1) swap(l, r);
int i = __builtin_ctz(r - l);
int j = l >> i;
cout << "? " << i << " " << j << endl;
int t;cin >> t;
sum += t * flag;
}
sum = (sum % 100 + 100) % 100;
cout << "! " << sum << endl;
}
标签:pre,AtCoder,Beginner,int,sum,然后,355,query,100
From: https://www.cnblogs.com/orzkeyhacker/p/18226194