OIFC 2025.11.14 模拟赛总结
没挂分。
T1 蛇
题目描述
你获得了一个长度为 \(n\) 的字符串 \(a\),它的每一个位置都印有一个字符 o 或者 x。长期看着一个相同的字符串十分容易产生审美疲劳,所以你决定对它进行一些操作。
你选取了一个参数 \(k\)。则你可以任意次数的对字符串 \(a\) 进行如下两种操作之一(也可以不操作):
-
选择一个长度为 \(k\) 的区间 \([l,r]\)。将字符串中第 \(l\) 个到第 \(r\) 个位置(下标从 \(1\) 开始,下同)的字符取反。即
o变成x,x变成o。 -
选择一个长度为 \(k\) 的区间 \([l,r]\)。如果字符串中第 \(l\) 个到第 \(r\) 个位置的字符全部是一样的,就可以删除它们,然后剩余字符拼接起来。
为了完全理解第二种操作,这里有一个例子:
字符串 \(a\) 为 oxxoooxox,并且选取了参数 \(k = 3\)。那么可以对区间 \([4,6]\) 进行操作二,因为这个区间的字符全部都是 o。操作完成后,\(a\) 将会变成 oxxxox,这是 oxx 和 xox 拼接的结果。
非常不幸的是,字符串 \(a\) 被污染了。具体来说,某些位置变成了 ?,你不记得这个位置究竟是 o 还是 x 了,所以你认为都是有可能的。
你想要知道,有多少个字符串 \(b\) 可能被 \(a\) 通过上述两种操作表示出来?\(b\) 可以是空串。由于可能的 \(b\) 实在是太多了,所以你只需要输出答案对 \(10^9 + 7\) 取模后的结果。
数据范围:\(1 \leq T \leq 10\),\(1 \leq k \leq n \leq 10^5\),\(a_i \in \{o,x,?\}\)。
题解
T2 白日窃贼
题目描述
你获得了一个长度为 \(n\) 的序列 \(a_1,a_2,\cdots,a_n\),这个序列保证了 \(1 \sim n\) 中的每一个数字都仅出现了恰好一次。
因为序列不是有序的,所以你决定将它升序排序。具体来说,每一次你可以交换两个数字。强大的你轻松观察到了使得序列升序排序的最优交换策略,所以为了不让你太骄傲,我需要对你稍加约束:在交换 \(a_x,a_y\) 的时候,需要满足 \(|a_x - a_y| = k\)。
你敏锐的发现对于所有的 \(k\),并不是每一个都可以成功的使序列排序。所以你需要对于每一个 \(k(1 \leq k \leq n )\),判断是否有解。如果有解,输出使序列排序的最少交换次数。
数据范围:\(1 \leq n \leq 10^5\)。
题解
注意到 \(k = 1\) 就相当于逆序对个数,容易证明。
打开大样例,发现有大量的 \(-1\),并且在 \(k=1,2,3,4,6,9,12,18,36\) 时有解。
于是可以直接猜出来当 \(k\) 是 \(\gcd(|a_i - i|)\) 的因数时有解。
接下来,根据 \(k = 1\) 时时逆序对和小样例,容易发现是对于所有模 \(k\) 同余的 \(a_i\) 分别求逆序对再相加。
因此写一个树状数组求逆序对就行了。注意清空树状数组不要用 memset,怎加的怎么删就行。
时间复杂度为 \(\mathcal{O}(n \log n \times d(n))\),其中 \(d(n)\) 表示 \(n\) 以内的数最多有多少因数。
参考代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)){
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
inline void write(int x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
return;
}
int t[100005];
inline int lowbit(int x){
return x & (-x);
}
inline void modify(int x, int k){
while(x <= 100001){
t[x] += k;
x += lowbit(x);
}
return;
}
inline int query(int x){
int res = 0;
while(x){
res += t[x];
x -= lowbit(x);
}
return res;
}
inline int getni(vector<int> v){
int res = 0;
for(auto p : v){
res += query(100001) - query(p);
modify(p, 1);
}
for(auto p : v) modify(p, -1);
return res;
}
int n, a[100005];
vector<int> v[100005];
inline int solve(int k){
for(int i = 1; i <= n; i++) v[a[i] % k].push_back(a[i]);
int res = 0;
for(int i = 0; i < k; i++){
res += getni(v[i]);
v[i].clear();
}
return res;
}
signed main(){
freopen("snow.in", "r", stdin);
freopen("snow.out", "w", stdout);
n = read();
for(int i = 1; i <= n; i++) a[i] = read();
int ans = abs(a[1] - 1);
for(int i = 2; i <= n; i++) ans = __gcd(abs(a[i] - i), ans);
for(int i = 1; i <= n; i++){
if(ans % i == 0){
write(solve(i));
}else{
write(-1);
}
putchar('\n');
}
return 0;
}
T3 树形分流器
题目描述
咕咕咕。
题解
结论:不管 \(l,r\) 具体是多少,对于同一个点 \(v\),\(f(v,l,r)\) 取到最优值的策略都是相同的。
定义策略为将子树内所有非叶子结点的 \(a\) 调节成 \(l \sim r\) 的排列中,所有 \(a_i\) 在排列中的排名。
假设最优策略是 \(a_i \leftarrow l + p_i\),则 \(f\) 是 \(\sum (a_i - l - p_i)^2\),拆开相减后发现大部分都抵消了,\(l\) 相关的可以直接计算,\(p_i\) 相关的只有 \(\sum p_i\),由于 \(p\) 是排列所以也可算。至于 \(a_i\),我们需要维护 \(u\) 子树内 \(a\) 的和。显然这可以用 dfn + BIT 解决,于是就做完了。
参考代码:
#include<bits/stdc++.h>
#define int __int128
using namespace std;
namespace IO{
const int SIZE=1<<21;
static char ibuf[SIZE],obuf[SIZE],*iS,*iT,*oS=obuf,*oT=oS+SIZE-1;
int qr;
char qu[55],c;
bool f;
#define getchar() (IO::iS==IO::iT?(IO::iT=(IO::iS=IO::ibuf)+fread(IO::ibuf,1,IO::SIZE,stdin),(IO::iS==IO::iT?EOF:*IO::iS++)):*IO::iS++)
#define putchar(x) *IO::oS++=x,IO::oS==IO::oT?flush():0
#define flush() fwrite(IO::obuf,1,IO::oS-IO::obuf,stdout),IO::oS=IO::obuf
#define puts(x) IO::Puts(x)
template<typename T>
inline void read(T&x){
for(f=1,c=getchar();c<48||c>57;c=getchar())f^=c=='-';
for(x=0;c<=57&&c>=48;c=getchar()) x=(x<<1)+(x<<3)+(c&15);
x=f?x:-x;
}
template<typename T>
inline void write(T x){
if(!x) putchar(48); if(x<0) putchar('-'),x=-x;
while(x) qu[++qr]=x%10^48,x/=10;
while(qr) putchar(qu[qr--]);
}
inline void Puts(const char*s){
for(int i=0;s[i];++i)
putchar(s[i]);
putchar('\n');
}
struct Flusher_{~Flusher_(){flush();}}io_flusher_;
}
using IO::read;
using IO::write;
int n, q, l[2000005], r[2000005], a[2000005], dfn[2000005], sz[2000005], timer = 0;
namespace BIT{
int t[2000005];
inline int lowbit(int x){
return x & (-x);
}
inline void modify(int x, int k){
while(x <= 2 * n - 1){
t[x] += k;
x += lowbit(x);
}
return;
}
inline int query(int x){
int res = 0;
while(x){
res += t[x];
x -= lowbit(x);
}
return res;
}
}
inline void dfs(int u){
if(!u) return;
dfn[u] = ++timer;
dfs(l[u]), dfs(r[u]);
sz[u] = 1 + sz[l[u]] + sz[r[u]];
return;
}
signed main(){
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
read(n), read(q);
for(int i = 1; i <= 2 * n - 1; i++) read(l[i]), read(r[i]);
dfs(1);
while(q--){
int u, y, v, l1, r1, l2, r2;
read(u), read(y), read(v), read(l1), read(r1), read(l2), read(r2);
BIT::modify(dfn[u], -a[u]), a[u] = y, BIT::modify(dfn[u], a[u]);
write((l1 * l1 - l2 * l2) * (r1 - l1) - 2 * (l1 - l2) * (BIT::query(dfn[v] + sz[v] - 1) - BIT::query(dfn[v] - 1)) + 2 * (l1 - l2) * (r1 - l1) * (r1 - l1 + 1) / 2);
putchar('\n');
}
return 0;
}

浙公网安备 33010602011771号