C
贪心
每次可以选一个子序列,全部减一。问最少多少次操作可以变成不减。
只能减不能加,那么贪心的想,最后一个元素一定不能减,否则它变小了,可能需要前面的元素减更多次才能保证序列不减。
所以处理完一个后缀之后,
一定比后缀里的所有都要小,也就是比
小。如果初始
,需要减小到
,如果本来就比
小则不用操作。
最后我们每次可以操作一个子序列,也就是同时操作多个。所以操作次数上界取决于需要操作次数最多的那个元素。
#include <bits/stdc++.h>
#define int long long
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using pii = pair<int, int>;
using graph = vector<vector<pii>>;
using ugraph = vector<vector<int>>;
constexpr int N = 2E5 + 5;
constexpr double eps = 1E-8;
constexpr int inf = 0x3f3f3f3f;
constexpr ll INF = 1E18;
void solve() {
int n; cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
int Max = -INF;
int ans = -INF;
for (int i = 1; i <= n; i++) {
Max = max(a[i], Max);
ans = max(ans, Max - a[i]);
}
cout << ans << "\n";
}
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T = 1; cin >> T;
for (int ttt = 1; ttt <= T; ++ttt) {
solve();
}
return 0;
}
F
倍增
每次都可以把,
任选。问操作不超过
次,可以得到的
最小值
注意到这个操作,每次如果,每次都会让整个值域折半。所以很快就会所有元素变成一样的,或者只有
两种值
最多次就收敛了,所以直接暴力模拟
次左右。此时的数组一定已经收敛了
扫描线计算答案即可
#include <bits/stdc++.h>
#define int long long
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using pii = pair<int, int>;
using graph = vector<vector<pii>>;
using ugraph = vector<vector<int>>;
constexpr int N = 2E5 + 5;
constexpr double eps = 1E-8;
constexpr int MOD = 998244353;
constexpr ll INF = 1E18;
void solve() {
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
int Max = *max_element(1 + a.begin(), a.end());
int Min = *min_element(1 + a.begin(), a.end());
int cnt = 0;
int Time = 35;
while (cnt < min(Time, n)) {
int v = (Min + Max) / 2;
for (int i = 1; i <= n; i++) {
a[i] = abs(a[i] - v);
}
Max = *max_element(1 + a.begin(), a.end());
Min = *min_element(1 + a.begin(), a.end());
cnt++;
}
sort(1 + a.begin(), a.end());
vector<int> pre(n + 1);
for (int i = 1; i <= n; i++) pre[i] = pre[i - 1] + a[i];
int ans = 0;
for (int i = 1; i <= n; i++) {
// cout << a[i] << " " << pre[i] << endl;
ans += ((a[i] * i % MOD - pre[i]) % MOD + MOD) % MOD;
ans %= MOD;
}
// cout << endl;
cout << ans << "\n";
}
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T = 1;
for (int ttt = 1; ttt <= T; ++ttt) {
solve();
}
return 0;
}
I
矩阵快速幂优化dp
表达式求值问题,不用考虑运算优先级。显然是无后效性的,一般可以考虑dp,只要算出来前缀的值就行。
这个,先考虑加法和乘法。这很常规,假设前缀不同表达式个数为
,加法就是给每个都加当前数,
。乘法则是给所有前缀都乘上
,
比较麻烦的是还有位运算。但是,所以位运算只会影响到低
位。可以增加状态,记录低
的
,就能算出来位运算的影响。比如按位与,就是高位丢弃掉,低位变成
mask & x
,按位与和按位异或,都是高位不变,低位变成mask^x,mask|x
这需要我们维护低位为的方案数。这并不困难,就是状压
记录方案素的思路,当前
会转移到运算后的新
,需要注意的是,五种运算,都会带来
的变化,不要漏了
+,*
,而且这两种运算后,可能超过四位,需要截取低四位。
最后很大,需要矩阵快速幂优化
。我们需要把这个
转移,转成矩阵表示。如果我们采用矩阵左乘状态向量的话,状态向量需要是个列向量,并且矩阵里
,表示
这个转移,不要搞反行列。
void solve()
{
int n,k;
cin>>n>>k;
vi a(k);
for(auto &x:a){
cin>>x;
}
int all=(1<<4);
int sum=16,cnt=17;
Matrix f(2+all,vi(2+all));
for(int i:a){
f[sum][sum]+=i;
f[sum][cnt]+=i;
f[sum][sum]++;
for(int mask=0;mask<all;mask++){
f[i&mask][mask]++;
f[i|mask][mask]++;
f[i^mask][mask]++;
f[sum][mask]+=(i&mask);
f[sum][mask]+=((i|mask)-mask);
f[sum][mask]+=((i^mask)-mask);
f[(i+mask)&(all-1)][mask]++;
f[(i*mask)&(all-1)][mask]++;
}
f[sum][sum]+=2;
f[cnt][cnt]+=5;
}
rep(i,0,2+all-1){
// cout<<i<<":";
rep(j,0,2+all-1){
f[i][j]=(f[i][j]%M2+M2)%M2;
// cout<<f[i][j]<<' ';
}
// cout<<'\n';
}
Matrix f0(2+all,vi(1));
for(int i:a){
f0[sum][0]+=i;
f0[cnt][0]++;
f0[i][0]++;
}
Matrix g=power(f,n-1,M2);
Matrix res=multiply(g,f0,M2);
// rep(i,0,17){
// cout<<i<<' '<<res[i][0]<<'\n';
// }
int ans=res[sum][0];
// cout<<ans<<' ';
ans=ans*inv(res[cnt][0],M2)%M2;
cout<<ans<<'\n';
}
J
数论,求
太大了。一种思路是分解质因子,然后每个因子的指数取
。最后知道
的每个因子,再用快速幂或者欧拉降幂来计算
这样需要快速分解大小的数。考虑
。但还是太慢了,多测会寄。
实际上在这里就是个暴力的错解。正解是数学,很快。
么此都可以把提出来,求
是个下降很快的,最多
次就会降低到
,所以递归
层,每层算一下
。总复杂度