2025.08.04 HDU 多校ACM
1004. 传送排序
tag: DP
我们可以发现,除了在序列的最前面插入,其他在一个数前面插入,都可以用在一个数后面插入去替代,而最前面插入可以通过在原序列的最前面加入一个 \(0\) 来处理
同时另外有一个小性质,就是显然任何一个序列最多只需要 \(n\) 次操作即可
然后我们就可以设计 \(DP\) 了
记 \(pos_i\) 表示编号为 \(i\) 的数在原序列中的下标
设 \(f_i\) 表示,编号为 \(i\) 的猫没有移动,排序完 \(1\sim i\) 的猫的位置需要的最少的操作次数
而这个式子的主要部分很特别 \(f_i = f_j+i-j\)
我们移项之后发现,我们需要比较的是 \(f_i -i\) 和 \(f_j - j\) 的大小
我们发现,我们可以用树状数组来加速查询操作
具体地,我们每次在下标为 \(pos\) 处加入一个 \(f_i - i\),查询就是查询前缀最小值即可
最后的时间复杂度 \(O(n\log n)\)
code
#include<bits/stdc++.h>
using namespace std;
const int NN = 2e5 + 8,INF = 0x3f3f3f3f;
int T;
int n;
int a[NN],pos[NN];
int f[NN];
int b[NN];
inline int lowbit(int x){return x & (-x);}
inline void add(int x,int num){
while(x <= n){
b[x] = min(b[x],num);
x += lowbit(x);
}
}
inline int ask(int x){
int ans = INF;
while(x){
ans = min(ans,b[x]);
x -= lowbit(x);
}
return ans;
}
int main(){
ios::sync_with_stdio(false),cin.tie(0);
cin >> T;
while(T--){
cin >> n;
for(int i = 1; i <= n; ++i){
cin >> a[i];
b[i] = INF;
pos[a[i]] = i;
}
for(int i = 1; i <= n; ++i){
if(pos[i-1] < pos[i]) f[i] = f[i-1];
else f[i] = i;
int ak = ask(pos[i]);
if(f[i]-i > ak) f[i] = ak + i;
add(pos[i],f[i]-i);
}
int ans = min(n,f[n]);
for(int i = 1; i < n; ++i){
ans = min(ans,f[i] + n - i + 1);
}
cout << ans << '\n';
}
}
1011. 取模
有一点小思路可以快速排除一些数,首先最后取模操作做完之后所有的数的和是 \(\frac n c\) 的倍数
然后我们可以用暴力的手段,在 \(O(m\ln m)\) 的时间内快速求出对于所有 \(k\),操作完之后的所有数的和
介于题目中的 \(c\) 给得很小,所以说按概率这样操作,差不多只会剩下 \(c\) 个数
又因为题目给的 \(c\) 比较小,所以我们只需要对于这 \(c\) 个数,每一个都做一个 \(O(n)\) 的暴力判断即可
最后的时间复杂度 \(O(c\sum n)\)
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int NN = 5e5 + 8;
int T;
int n,m,c;
int a[NN];
int pre[NN];
ll sum;
vector<int> ans;//答案
int cnt[NN];
void check(int x){
bool vis = 1;
int tot = 0;
for(int i = 1; i <= n; ++i){
int num = a[i] % x;
if(cnt[num] == 0) ++tot;
++cnt[num];
if(tot > c || cnt[num] > n/c){
vis = 0;break;
}
}
if(vis) ans.push_back(x);
for(int i = 1; i <= n; ++i)
cnt[a[i]%x] = 0;
}
int main(){
ios::sync_with_stdio(false),cin.tie(0);
cin >> T;
while(T--){
cin >> n >> m >> c;
ans.clear();
sum = 0;
for(int i = 0; i <= m; ++i) pre[i] = 0;
for(int i = 1; i <= n; ++i){
cin >> a[i];
++pre[a[i]];
sum += a[i];
}
sort(a+1,a+1+n);
bool vis = 1;
for(int i = 1; i <= n; i += n/c)//特判-1
if(a[i] != a[i + n/c - 1] || (i != 1 && a[i] == a[i-1])){
vis = 0;break;
}
if(vis){
cout << "-1\n";continue;
}
for(int i = 1; i <= m; ++i)//预处理每个数的出现次数
pre[i] += pre[i-1];
ll del = 0;
if(c == 1) ans.push_back(1);
for(int i = 2; i <= m; ++i){
del = 0;
for(int j = 1; i * j <= m; ++j){
del += 1ll * j * (pre[min(m,i*(j+1)-1)]- pre[i*j-1]);
}
if((sum - del * i) % (n / c) == 0) check(i);
}
cout << ans.size() << ' ';
for(int i = 0; i < ans.size(); ++i){
cout << ans[i] << ' ';
}
cout << '\n';
}
}
1012. cats 的加减乘除
首先,对于一个算式如果有加减号的话,把加号变减号,减号变加号,那么一定会有另外一个算式,他们两个计算的结果相加,在第一个加减号后面的部分会抵消
比如:
\(1+2\times 3\times 3+5\) 和 \(1-2\times 3\times 4-5\)
最后就只用考虑最前面的1,这样就好做了
那么我们的问题就转化为,求在第一个加减号之前的乘积的期望值
没有 \(-1\) 显然是简单的,答案就是:\(a_1\prod_{i=2}^n (a_i+\frac 1 {a_i})\)
那么有 \(-1\) 怎么做呢?
我们的问题就转化为了求在给定的数中选 \(k\) 个数的乘积的期望
显然这可以用生成函数做,答案就是 \((\prod_{i = 1}^{n} 1+(b_i+\frac 1 {b_i})x)[x^k]\),至于实现?分治 \(NTT\) 即可
但是,我们发现 \(-1\) 如果出现在第一个位置,那么默认是做乘法的,那该怎么做?
我们可以枚举第一个数是哪一个,那么我们的答案就变成了 \(\sum_{i = 1}^n \frac{b_i x}{1+(b_i+\frac 1 {b_i})x}\prod_{i = 1}^{n} 1+(b_i+\frac 1 {b_i})x[x^k]\)
我们发现 \(\sum_{i = 1}^n \frac{b_i x}{1+(b_i+\frac 1 {b_i})x}\) 通分之后,分母与右侧相同,而分子的求解同样可以用分治 \(NTT\) 求解,这样我们就把这道题做完了
因为完整代码太长,所以我们删去了预处理部分和 多项式乘法(\(NTT\)) 板子
code
poly solve(int l, int r, poly& arr) {
if(r < l) return {1};
if(l == r) {
return {1, (arr[l] + inv(arr[l])) % mod}; // 多项式 1 + a_l x
}
int mid = (l + r) / 2;
poly left_poly = solve(l, mid, arr);
poly right_poly = solve(mid + 1, r, arr);
return left_poly * right_poly;
}
poly solve2(int l,int r,poly& arr,poly& ar2){
if(r < l) return {1};
if(l == r){
ar2 = {0,arr[l]};
return {1, (arr[l] + inv(arr[l])) % mod};
}
int mid = (l + r) / 2;
poly ar2l,ar2r;
poly left_poly = solve2(l, mid, arr, ar2l);
poly right_poly = solve2(mid + 1, r, arr, ar2r);
ar2 = ar2l * right_poly + ar2r * left_poly;
return left_poly * right_poly;
}
int inv4;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
pre();
inv2 = jcinv[2];
inv4 = jcinv[4] * jc[3] % mod;
int T;
cin>>T;
while(T--)
{
int n;
cin>>n;
int ans = 0,cnt = 0,now = 0;
for(int i = 1; i <= n; ++i){
cin >> p_[i];
if(p_[i] != -1) vis[p_[i]] = 1;
}
poly arr;arr.clear();
for(int i = 1; i <= n; ++i){
if(!vis[i]) arr.push_back(i);
}
poly get;
if(p_[1] != -1){
get = solve(0,arr.size()-1,arr);
for(int i = 0; i <= arr.size(); ++i){
get[i] = get[i] * invc(arr.size(),i) % mod;
}
now = p_[1];
}
else{
poly g = solve2(0,arr.size()-1,arr,get);
for(int i = 1; i <= arr.size(); ++i){
get[i] = get[i] * invc(i,1) % mod * invc(arr.size(),i) % mod;
}
now = 1;
++cnt;
}
for(int i = 2; i <= n; ++i){
// cout << now * get[cnt] % mod << endl;
ans = (ans + now * get[cnt] % mod * inv2 % mod) % mod;
if(p_[i] == -1) ++cnt;
else now = now * ((p_[i] + jcinv[p_[i]] * jc[p_[i]-1]) % mod) % mod;
now = now * inv4 % mod;
}
// cout << now * get[cnt] % mod << endl;
for(int i = 1; i <= n; ++i)
if(p_[i] != -1) vis[p_[i]] = 0;
cout << (ans + now * get[cnt] % mod + mod * 2ll) % mod << endl;
}
return 0;
}
本文来自博客园,作者:ricky_lin,转载请注明原文链接:https://www.cnblogs.com/rickylin/p/19023889/2025_08_05--HDU

浙公网安备 33010602011771号