10.5 模擬賽題解報告
pts: 100 + 100 + 0 = 200
T1 ad
題目描述
給定 \(N\) 個數 \(a_1, a_2 \dots a_n\),現在對每個數都加 \(K\) 或者減 \(K\),求操作操作之後的最大值減去最小值的差最小。
\(1 \leq N \leq 10^5, 1 \leq K, a_i \leq 10^9\)
solution
40pts:
\(N \leq 10\)
直接 \(2^N\) 暴力列舉就好了。
100pts
不難發現答案一定可以為 \(Max(a_i) - Min(a_i)\), 也就是所有值的都加 \(k\) 或者都減 \(k\) 的情況。
然後預處理出兩個陣列,\(a_i = num_i + k, b_i = num_i - k\)
然後就是用 \(b\) 陣列中的某些數去替換 \(a\) 陣列中的某些數。
然後你會驚奇的發現一定是從頭往後依次替換掉 \(a\) 中的數一定是最優的,然後你只需要在替換過程中更新答案就好了。
code
/* work by:Ariel_ Knowledge: */ #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #define int long long using namespace std; const int MAXN = 1e5 + 5; int a[MAXN], b[MAXN], num[MAXN], n, k, Ans; signed main(){ scanf("%lld%lld", &n, &k); for (int i = 1; i <= n; i++){ scanf("%lld", &num[i]); a[i] = num[i] - k, b[i] = num[i] + k; } sort(a + 1, a + n + 1), sort(b + 1, b + n + 1); Ans = a[n] - a[1]; for (int i = 1; i < n; i++) { Ans = min(Ans, max(b[i], a[n]) - min(b[1], a[i + 1])); } cout<<Ans; system("pause"); return 0; }
T2 cutter
題目大意
給你一個長為 \(L\) 的字串,然後給你 \(N\) 個英文單詞,現在給這個英文句子斷句,要求斷完句之後每一段都是一個英文單詞,求斷句的所有方案。
\(L\leq 200, N\leq 30\)
solution
設 \(f_i\) 表示字串前 \(i\) 個字元斷句的方案數。
轉移就列舉最後一次斷句的位置 \(j\),只有 \(j + 1 \sim i\) 這段是一個單詞,並且 \(f_j\) 這個狀態被更新過才能轉移。這個 \(hash\) 就可以實現。
然後怎麼統計方案呢?
深受昨晚牛客 A 題打擊。
\(dp\) 時候,能轉移狀態的時候就從 \(j\) 向 \(i\)
code
/*
work by:Ariel_
Sorce:
Knowledge:
Time:
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <map>
#include<vector>
#include <algorithm>
#define int long long
using namespace std;
const int MAXN = 210;
const int mod = 998244353;
const int seed = 30;
int read() {
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}
while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
return x * f;
}
int f[MAXN], n, base[MAXN], _hash[MAXN];
char s[MAXN], t[MAXN];
map<int, bool>mp;
int get(char c) {return c - 'a' + 1;}
vector<int>vec[MAXN];
int tmp[MAXN], tot, L;
bool vis[MAXN], fag[MAXN][MAXN];
void dfs(int u, int tot) {
tmp[tot] = u;
if(u == L) {
for(int i = 1; i <= L; i++) {
cout<<s[i];
if(i != L) {
for (int j = 1; j <= tot; j++) {
if(tmp[j] == i) cout<<"_";
}
}
}
puts("");
return ;
}
for (int i = 0; i < vec[u].size(); i++) {
int v = vec[u][i];
dfs(v, tot + 1);
}
}
signed main(){
scanf("%s", s + 1);
L = strlen(s + 1);
for (int i = 1; i <= L; i++) _hash[i] = (_hash[i - 1] * seed % mod + get(s[i])) % mod;
n = read();
base[0] = 1;
for (int i = 1; i <= n; i++) base[i] = base[i - 1] * seed % mod;
for (int i = 1; i <= n; i++) {
scanf("%s", t + 1);
int Hash = 0, len = strlen(t + 1);
for (int j = 1; j <= len; j++) {
Hash = (Hash * seed % mod + get(t[j])) % mod;
}
mp[Hash] = 1;
}
f[0] = 1;
for (int i = 1; i <= L; i++) {
for (int j = 1; j <= i; j++) {
if(f[j - 1]) {
int H = 0;
for (int k = j; k <= i; k++) H = (H * seed % mod + get(s[k])) % mod;
if(!mp[H]) continue;
f[i] = f[i] + f[j - 1];
vec[j - 1].push_back(i);
}
}
}
if(f[L] == 0) {return 0;}
dfs(0, 1);
return 0;
}
T3
題目大意
總共由 \(N\) 家商店排成一行,第 \(i\) 家商店的物價為 \(v_i\)。小蔥購物從第 \(a\) 家商店走到第 \(b\) 家商店。在走的過程可以做下面事情的任意一件:
- 購買一件商品
- 賣出一件商品,要求這個時候小蔥手上至少有一件商品才能賣出
- 路過啥也不幹
\(m\) 次詢問,每次有兩種操作,修改某個商店的物價或者求出從 \(a\) 到 \(b\) 的最大收益。
假設它的本金無限,但是他只能帶著一件商品,並且賣出的時候要保證手裡面有商品。
\(1 \leq N, M \leq 10^5, 1 \leq a, b \leq N, 1\leq v_i, v \leq 10^4\)
solution
這題好像全網沒有題解,所以還是寫詳細點吧。
30pts dp
對每次詢問都 \(dp\) 一次。
設 \(f_i\) 表示到達第 \(i\) 商店的最大收益。
轉移的時候就考慮當前是不動還是賣物品。不可能買物品,因為如果最後買了物品都賣不掉了。
啥也不幹的話就是 \(f_i = f_{i - 1}\)
如果要賣物品的話就要從前面找出一個能賣物品的最優狀態,這個東西 \(dp\) 的時候順便記錄以下就好了。
\(f_i = Max + val[i]\)
\(Max = max(Max, f[i - 1] - val[i])\)
從右向左走就是列舉順序不同。
複雜度 \(O(nm)\)
code
//if(opt == 1) work(l, r)
int work(int l, int r) {
memset(f, 0, sizeof f);
if(l <= r) {
int Max = -val[l];//找出前面可以賣東西的最優狀態
for (int i = l + 1; i <= r; i++) {
f[i] = max(f[i - 1], Max + val[i]);
Max = max(Max, f[i - 1] - val[i]);
}
return f[r];
}
else {
swap(l, r);
int Max = -val[r];
for (int i = r - 1; i >= l; i--) {
f[i] = max(f[i + 1], Max + val[i]);
Max = max(Max, f[i + 1] - val[i]);
}
return f[l];
}
}
100pts
下面是 cgp 學長程式碼裡面的註釋:
價格是一個波動的序列,所以根據貪心的策略,要在最高點賣,最低點買,這樣的差是最大的,所以用樹狀陣列維護區間和,把一個區間裡面那些最高點的和和最低點的相反數維護出來。
#include<bits/stdc++.h>
using namespace std;
int read() {
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}
while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
return x * f;
}
int n,m;
namespace PT1 {
const int N=100000+100;
int a[N],tree1[N],tree2[N],bz1[N],bz2[N],ma[N],nn;
int lowbit(int x) {
return x&-x;
}
void update(int *tree,int pos,int c) {
while(pos<=n) {
tree[pos]+=c;
pos+=lowbit(pos);
}
}
int query(int *tree,int pos) {
int ret=0;
while(pos) {
ret+=tree[pos];
pos-=lowbit(pos);
}
return ret;
}
void updatep_v(int i) {
if(i<=1||i>=n) return ;
int tmp=query(tree1,i)-query(tree1,i-1);
update(tree1,i,-tmp);
tmp=query(tree2,i)-query(tree2,i-1);
update(tree2,i,-tmp);
bz1[i]=0;
bz2[i]=0;
if(a[i]>a[i-1]&&a[i]>=a[i+1]) bz1[i]=1,update(tree1,i,a[i]);
if(a[i]<=a[i-1]&&a[i]<a[i+1]) bz1[i]=-1,update(tree1,i,-a[i]);
if(a[i]>=a[i-1]&&a[i]>a[i+1]) bz2[i]=1,update(tree2,i,a[i]);
if(a[i]<a[i-1]&&a[i]<=a[i+1]) bz2[i]=-1,update(tree2,i,-a[i]);
}
int main() {
for(int i=1; i<=n; ++i) a[i]=read();
for(int i=2; i<n; ++i) {
if(a[i]>a[i-1]&&a[i]>=a[i+1]) bz1[i]=1,update(tree1,i,a[i]);
if(a[i]<=a[i-1]&&a[i]<a[i+1]) bz1[i]=-1,update(tree1,i,-a[i]);
if(a[i]>=a[i-1]&&a[i]>a[i+1]) bz2[i]=1,update(tree2,i,a[i]);
if(a[i]<a[i-1]&&a[i]<=a[i+1]) bz2[i]=-1,update(tree2,i,-a[i]);
}
while(m--) {
int opt=read();
if(opt==1) {
int l=read(),r=read();
if(l==r) {
cout<<0<<'\n';
continue;
}
int ans=0;
if(l<r) {
if(a[l]<a[l+1]) ans-=a[l];
if(a[r]>a[r-1]) ans+=a[r];
ans+=query(tree1,r-1)-query(tree1,l);
} else {
swap(l,r);
if(a[r]<a[r-1]) ans-=a[r];
if(a[l]>a[l+1]) ans+=a[l];
ans+=query(tree2,r-1)-query(tree2,l);
}
printf("%d\n",ans);
} else {
int p=read(),w=read();
a[p]=w;
updatep_v(p-1),updatep_v(p),updatep_v(p+1);
}
}
return 0;
}
}
int main() {
n=read(),m=read();
PT1::main();//100pts
return 0;
}