中值定理
模型:如果一个序列 长度为 ,,需要找到一个长度为 的子序列 使得 ,且 ,那么可以这样考虑:对于 一定存在至少一个序列满足条件。这是个可以二分的结构。二分的时候以 为单位长度,令 ,若 ,那么继续在 上二分。否则在 上二分。
UER #11 B
【题意】给定一个长度为 的序列,要求拿出其中的 个数,使得它们加起来是 的倍数。
【分析】
令命题 表示对于任意数组 都符合题意并且找到了一个构造方法。
若 为合数,不妨设 。假设 ,那么考虑首先对 不断取前 个数,然后找出 个符合条件的数合并成一个 的倍数,再将其他数丢回去,直到剩下的数没有 个为止。注意到 ,因此可以找出 个 的倍数,剩下可以扔掉。然后对这些数做 就得到了一组合法解。因此只需要考虑素数情形。
素数怎么办。考虑 。考虑对这 个数采用不断选众数和第二多的数的方式,分成 个二元组和一个剩下来的数,其中每个二元组都满足其中两个数不一样。
我们选上这个数。另外选上每个二元组第一个数。得到一个 个数的选择方案,加起来为 。我们的问题转化为了,有 个数 (第二个数与第一个数的差),选出一个子集满足加起来模 余 。
先考虑证明一定存在一种选法。我们归纳地证明。
假设前 个数选出的子集和可以表出的数集合为 。定义集合 表示 。那么考虑证明: 并且 不是 的完全剩余系的情况下 。
这个怎么证,反证法。假设成立,那么考虑 有:,但是我们令 ,有 ,矛盾。因此原命题成立。
这个命题代表了什么? 不是 的完全剩余系的情况下 !因此对于 , 一定是 的完全剩余系,因为每次会增长至少 个数。
我们考虑构造答案。首先我们让每次增长恰好 个数,则比较容易记录答案。暴力增长是 的。我们考虑采用中值定理优化。
首先我们有个非常巧妙的观察:若 ,那么一定可以将 放进去!并且你可以找到一个 使得 。因此可以直接中值定理二分,。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
void cmax(int &x, int y) {if(x < y) x = y;}
void cmin(int &x, int y) {if(x > y) x = y;}
//调不出来给我对拍!
int a[700010];
bitset<300000> bs;
vector<int> v[300100];
set<pii> s;
pair<int, int> p;
int nsum = 0;
vector<int> ts;
vector<int> pre;
vector<int> add;
int tim[300100];
int ch[300100];
int n;
int inv[300100];
int qpow(int x, int k) {
int ans = 1;
while(k) {
if(k & 1) ans = ans * x % n;
x = x * x % n;
k >>= 1;
}
return ans;
}
int bin(int i, int j, int d) {
int l = i * inv[d]; int r = j * inv[d] - 1;
while(r < l) r += n;
while(l < r) {
int mid = (l + r + 1) >> 1;
if(!bs[mid * d % n]){r = mid - 1; }
else { l = mid; }
}
return (r + 1) * d % n;
}
set<int> cp, uc; //选了,没选
signed main() {
ios::sync_with_stdio(0);
cin.tie(NULL);
cout.tie(NULL);
//time_t start = clock();
//think twice,code once.
//think once,debug forever.
cin >> n;
f(i, 1, n - 1) inv[i] = qpow(i, n - 2);
f(i, 1, 2 * n - 1) cin >> a[i];
f(i, 1, 2 * n - 1) a[i] %= n;
f(i, 1, 2 * n - 1) { v[a[i]].push_back(i); }
f(i, 0, n - 1) { if(v[i].size() >= n) {f(j, 0, n - 1) {cout << v[i][j] << " ";} cout << endl; return 0;}}
f(i, 0, n - 1) { s.insert({v[i].size(), i}); }
ts.push_back(0); pre.push_back(0);
f(i, 1, n - 1) {
pii cur = (*(--s.end())); s.erase(--s.end());
pii urc = (*(--s.end())); s.erase(--s.end());
// cout << cur.second << " " << urc.second << endl;
nsum += cur.second; ch[cur.second] ++; nsum %= n;
ts.push_back((urc.second - cur.second + n) % n); pre.push_back(cur.second);
if(cur.first > 1) {s.insert({cur.first - 1, cur.second});}
if(urc.first > 1) {s.insert({urc.first - 1, urc.second});}
}
pii nct = (*(--s.end()));// cout << nct.second << endl; //assert(s.size() == 1);
nsum += nct.second; ch[nct.second] ++; nsum %= n; //cout << nsum << endl;
int tar = (n - nsum) % n;
// cout << tar << endl;
bs[0] = 1; tim[0] = 0;
cp.insert(0); f(i, 1, n - 1) uc.insert(i);
int now = 1;
while(!bs[tar]) {
// f(i, 0, 3) {
int t = bin((*(cp.begin())), (*(uc.begin())), ts[now]);
uc.erase(t); cp.insert(t);
bs[t] = 1; tim[t] = now;
// cout <<"add : "<< t << endl;
// tim[t] = now;
now++;
// }
}
int ncp = tar; int ccp = tim[ncp];
while(1) {
if(ccp <= 0) break;
// cout << ncp << " " << ccp << endl;
ch[pre[ccp]] --; ch[(pre[ccp] + ts[ccp]) % n] ++;
ncp = (ncp - ts[ccp] + n) % n; ccp = tim[ncp];
}
f(i, 0, n - 1) { f(j, 0, ch[i] - 1) cout << v[i][j] << " ";}
cout << endl;
//time_t finish = clock();
//cout << "time used:" << (finish-start) * 1.0 / CLOCKS_PER_SEC <<"s"<< endl;
return 0;
}
[WC2023] 楼梯
也可以用中值定理优化 的找区间子区间的过程,但是这题直接保证了 。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App