比賽連結:牛客周賽 Round 38
A:小紅的正整數自增
void solve(){ ll n; cin >> n; for(int i = 0; i <= 9; i ++){ ll y = n + i; if(y % 10 == 0){ cout << i << '\n'; return; } } }
B:小紅的拋棄字尾
思路:9的倍數數位和也是9的倍數,模擬即可
void solve(){ string s; cin >> s; ll sum = 0; for(auto c : s){ int a = c - '0'; sum += a; } ll sum1 = 0, ans = 0; for(int i = s.size() - 1; i >= 0; i --){ if((sum - sum1) % 9 == 0) ans ++; int a = s[i] - '0'; sum1 += a; } cout << ans << '\n'; }
C:小紅的字串構造
思路:因為給出的k<=n/2,所以先每兩個一組,剩下的位不出現迴文即可
void solve(){ ll n, k; cin >> n >> k; for(int i = 1; i <= k; i ++){ int u = i % 3; if(u == 0) cout << "aa"; else if(u == 1) cout << "bb"; else cout << "cc"; n -= 2; } for(int i = 1; i <= n; i ++){ int p = i % 22; char c = 'd' + p; cout << c; } }
D:小紅的平滑值插值
思路:如果沒有大於等於k的間隙就製造一個,如果有等於k沒有大於k的,就是0,如果有大於k的間隙就往中間插數字,使得間隙縮小,注意如果間隙是k的整數倍要-1
void solve(){ ll n, k; cin >> n >> k; vector<ll> a(n + 1); for(int i = 1; i <= n; i ++) cin >> a[i]; ll ans = 0, res = 0; for(int i = 2; i <= n; i ++){ ll cha = abs(a[i] - a[i - 1]); if(cha > k){ ans += cha / k; if(cha % k == 0) ans --; } else if(cha == k) res ++; } if(ans > 0) cout << ans << '\n'; else{ if(res == 0) cout << 1 << '\n'; else cout << 0 << '\n'; } }
E:小苯的等比數列
思路:列舉起始數字和公比即可
void solve(){ int n; cin >> n; int ans = 0; vector<int> a(n + 1), num(N); for(int i = 1; i <= n; i ++){ cin >> a[i]; num[a[i]] ++; ans = max(ans, num[a[i]]); } for(int i = 1; i < N; i ++){ for(int j = 2; j * i < N; j ++){ int now = i, cnt = 0; while(now < N && num[now]){ now *= j; cnt ++; } ans = max(ans, cnt); } } cout << ans << '\n'; }
F:小苯的迴文詢問
思路:記錄每一個數字的上一個出現的地址,用一個map來記錄每個數字出現的地址,對映到b來先處理出每個數字上次出現的位置,然後再從後往前看有沒有相鄰出現的情況,如果有就讓b[i] = b[b[i]],這樣就對映到了這個數字的相鄰位置的前一個的位置,最後維護一下區間最值,只要區間最值大於等於L即可,區間最值實現方法多種
struct ST{ int n, q; vector<vector<int>> f; ST(int _n) : n(_n), f(25, vector<int>(n + 1, 0)) {} void init(vector<int> & a){ for(int i = 1; i <= n; i ++) f[0][i] = a[i]; for(int j = 1; j <= 20; j ++){ for(int i = 1; i + (1 << j) - 1 <= n; i ++){ f[j][i] = max(f[j - 1][i], f[j - 1][i + (1ll << (j - 1))]); } } } int query(int l, int r){ int len = __lg(r - l + 1); return max(f[len][l], f[len][r - (1 << len) + 1]); } }; void solve(){ int n, q; cin >> n >> q; vector<int> a(n + 1), b(n + 1); for(int i = 1; i <= n; i ++) cin >> a[i]; map<int, int> la; for(int i = 1; i <= n; i ++){ if(la.count(a[i])){ b[i] = la[a[i]]; } la[a[i]] = i; } for(int i = n; i >= 1; i --){ if(b[i] == i - 1){ b[i] = b[b[i]]; } } // for(int i = 1; i <= n; i ++) cout << b[i] << ' '; ST st(n); st.init(b); while(q --){ ll l, r; cin >> l >> r; ll ans = st.query(l, r); if(ans >= l) cout << "YES\n"; else cout << "NO\n"; } }
G:小紅的區間刪除
思路:先思考逆序對的一種計算方法:一個數能過組成的逆序對就是它前面大於它的數字和它後面小於它的數字的數量之和
所以我們用維護兩個東西:字首和字尾
這裡用滑動視窗來維護到 i 的一個最大的刪除後滿足性質的區間,每次加上這個區間的長度,原因是可以發現,視窗的字首以及中間的都會隨著視窗的改變被計算過,所以只需要計算一個字尾的區間即可
每次刪除一個元素的時候要注意刪去它在字尾中小於這個數字的貢獻,再刪去字首中大於這個數字的貢獻
當釋放滑動視窗內的元素,加上這個元素字首中的貢獻和字尾中的貢獻
特殊的,當整個陣列的逆序數本身滿足大於等於k,不要忘記加上
struct BIT{ int n; vector<int> tr; BIT(int _n) : n(_n), tr(n + 10) {} int lowbit(int x) {return x & (- x);}; void add(int x, int y){ for(int pos = x; pos < n; pos += lowbit(pos)){ tr[pos] += y; } } ll query(ll x){ ll res = 0; for(int pos = x; pos; pos -= lowbit(pos)){ res += tr[pos]; } return res; } ll query(ll l, ll r){ return query(r) - query(l - 1); } }; void solve(){ int n, k; cin >> n >> k; vector<ll> a(n + 1); for(int i = 1; i <= n; i ++) cin >> a[i]; BIT bit1(1e6 + 10);//維護字尾 BIT bit2(1e6 + 10);//維護字首 ll sum = 0; for(int i = n; i >= 1; i --){ bit1.add(a[i], 1); sum += bit1.query(a[i] - 1); }//處理逆序對和字尾 int ans = (sum >= k); for(int i = 1, j = 1; i <= n; i ++){ bit1.add(a[i], -1);//刪掉ai sum -= bit1.query(a[i] - 1);//從字尾中刪掉比ai小的 sum -= bit2.query(a[i] + 1, 1e6);//從字首中刪掉(所有的-小於等於ai)即刪去字首中大於ai的 // cout << sum << '\n'; while(j <= i && sum < k){//小於,就要把aj補回來,注意是補入字首陣列 bit2.add(a[j], 1); sum += bit1.query(a[j] - 1); sum += bit2.query(a[j] + 1, 1e6); j ++; } ans += (i - j + 1); // cout << i << ' ' << j << '\n'; } cout << ans << '\n'; }