2020.07.27 牛客多校第六場
C. Combination of Physics and Maths
題意:
一個矩陣的底面積 \(S\) 定義為最後一行的數的和,重量 \(F\) 定義為所有數的和,給一個正整數矩陣,找一個“壓強“ \(p=\frac{F}{S}\) 最大的可非連續子矩陣,輸出 \(p\)。
思路:
當底面積確定時,重量越大,壓強越大,所以子矩陣的頂部一定是原矩陣的頂部。不確定的是子矩陣的底在哪一行,並且子矩陣選擇哪幾列。那麼列舉每一行作為底,依次選擇貢獻最大的列直到答案無法變大。如何計算貢獻?用字首和求出 \(sum[i][j] = \sum_{k=1}^isum[k][j]\) ,那麼在列舉 到某一行 \(i\)
複雜度為 \(O(Tnmlogm)\)。
#include<bits/stdc++.h> #define ll long long #define INF 0x3f3f3f3f #define LLINF 0x3f3f3f3f3f3f3f3f #define pii pair<int,int> #define vi vector<int> #define SZ(x) (int)x.size() #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; int a[205][205]; int sum[205][205]; bool cmp(pii x, pii y) { return 1ll * x.fi * y.se > 1ll * x.se * y.fi; } int main() { int t; scanf("%d", &t); while(t--) { int n, m; scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) scanf("%d", &a[i][j]); for(int j = 1; j <= m; j++) for(int i = 1; i <= n; i++) sum[i][j] = sum[i - 1][j] + a[i][j]; ll afz = 0, afm = 1; for(int i = 1; i <= n; i++) { vector<pii> tmp; for(int j = 1; j <= m; j++) tmp.pb(mp(sum[i][j], a[i][j])); sort(tmp.begin(), tmp.end(), cmp); ll fz = tmp[0].fi, fm = tmp[0].se; for(int j = 1;j<SZ(tmp);j++) { ll tmpz = fz + tmp[j].fi; ll tmpm = fm + tmp[j].se; if(fz * tmpm < fm * tmpz) { fz = tmpz; fm = tmpm; } else break; } if(afz * fm < afm * fz) { afz = fz; afm = fm; } } printf("%.8f\n", 1.0 * afz / afm); } }
J. Josephus Transform
題意:
給一個長度為 n 的排列和 m 次操作,每個操作可以表示為 (k, x),即進行 x 次以 k-約瑟夫變
換。問最後排列長啥樣。
思路:
對於一次k-約瑟夫變換,我們需要知道它的變化序列,如【1,2,3,4,5】的3-約瑟夫變換序列是【3,1,5,2,4】。
如何求變化序列?
設上一個被取出來的數字是當時的第 pos 個(初始設為 1 ),當前還剩下 cnt 個數字,那麼下一個被選出來的數應該是當前剩下的所有數字中的第 【\((pos+k-2)\% cnt + 1\) 】個。可以利用樹狀陣列或線段樹處理出這個變化序列,複雜度為 \(O(nlogn)\)
接著對於x次的k-約瑟夫變換,容易發現變換是滿足結合律的,所以可以自己寫一個快速冪,複雜度為 \(O(nlogx)\)。
所以對於m次查詢,總的複雜度為\(O(m(nlogn+nlogx))=O(nm(logn+logx))\)。
#include<bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define pii pair<int,int>
#define vi vector<int>
#define SZ(x) (int)x.size()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
const int MAXN = 1e5 + 5;
int n, m;
int lowbit(int x) {
return x & (-x);
}
vi multi(vi x, vi y) {
vi tmp(n + 1);
for(int i = 1; i <= n; i++)
tmp[i] = x[y[i]];
return tmp;
}
vi qpow(vi a, int x) {
vi ans(n + 1);
for(int i = 1; i <= n; i++)
ans[i] = i;
while(x) {
if(x & 1)
ans = multi(ans, a);
a = multi(a, a);
x >>= 1;
}
return ans;
}
int main() {
scanf("%d%d", &n, &m);
vi ans(n + 1);
for(int i = 1; i <= n; i++)
ans[i] = i;
while(m--) {
vi a(n + 1), c(n + 1);
for(int i = 1; i <= n; i++)
for(int j = i; j <= n; j += lowbit(j))
a[j]++;
int k, x;
scanf("%d%d", &k, &x);
int pos = 1;
for(int i = n; i >= 1; i--) {
pos = (pos + k - 2) % i + 1;
int L = 1, R = n;
while(L < R) {
int mid = (L + R) / 2;
int sum = 0;
for(int j = mid; j >= 1; j -= lowbit(j))
sum += a[j];
if(sum < pos)
L = mid + 1;
else
R = mid;
}
c[n - i + 1] = L;
for(int j = L; j <= n; j += lowbit(j))
a[j]--;
}
ans = multi(ans, qpow(c, x));
}
for(int i = 1; i <= n; i++)
printf("%d ", ans[i]);
}
K. K-Bag
題意:
k-bag 序列定義為由多個 1~ k 的排列順序連線起來的序列。
題目詢問給定序列是不是 k-bag 的連續子序列。
思路:
定義一個長度為 k 的 k-bag 為最小 k-bag,即1~k的某種排列。則合法的給定序列應該是由
【最小k-bag的一部分(頭部)+ 0至多個最小k-bag(中部) + 最小k-bag的一部分(尾部)】組成的。
首先預處理一些變數:
用 ex[i] 表示到 i 為止的 k 個數中(i<k 則為 i 個)是否有重複的數,有則 ex[i] = 0,否則 ex[i] = 1。
用 sum[i] 表示字首和,用 maxx[i] 表示字首最大值。
預處理的複雜度為 \(O(n)\)。
用 dp[i] 表示 i 是否可以作為合法的結束位置。
若 i 是”頭部“的合法結束位置,應滿足 ex[i] = 1 && maxx[i] <= k,合法則 dp[i] = 1。
若 i 是”中部“的合法結束位置,應滿足 ex[i] = 1 && dp[i-k] = 1 && sum[i] - sum[i-k] == k*(k+1)/2,合法則 dp[i] = 1。
若 i 是”尾部“的合法結束位置,與頭部類似,反向處理一遍即可,合法則 dp2[i] = 1。
因此總的複雜度還是 \(O(n)\)。
#include<bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define pii pair<int,int>
#define vi vector<int>
#define SZ(x) (int)x.size()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
const int MAXN = 5e5 + 5;
int n, k;
int a[MAXN];
int ex[MAXN];
unordered_map<int, int>num;
int dp[MAXN];
int maxx[MAXN];
ll sum[MAXN];
void solve1() {
int cnt = 0;
for(int i = 1; i <= min(n, k); i++) {
num[a[i]]++;
if(num[a[i]] == 2)
cnt++;
if(cnt > 0)
ex[i] = 0;
}
for(int i = min(n, k) + 1; i <= n; i++) {
if(i - k >= 1) {
num[a[i - k]]--;
if(num[a[i - k]] == 1)
cnt--;
}
num[a[i]]++;
if(num[a[i]] == 2)
cnt++;
if(cnt > 0)
ex[i] = 0;
}
for(int i = 1; i <= n; i++) {
maxx[i] = max(maxx[i - 1], a[i]);
sum[i] = sum[i - 1] + 1ll * a[i];
}
}
int ex2[MAXN];
unordered_map<int, int>num2;
int maxx2[MAXN];
int dp2[MAXN];
void solve2() {
int cnt = 0;
for(int i = n; i >= 1 && n - i + 1 <= k; i--) {
num2[a[i]]++;
if(num2[a[i]] == 2)
cnt++;
if(cnt > 0)
ex2[i] = 0;
}
for(int i = n; i >= 1; i--)
maxx2[i] = max(maxx2[i + 1], a[i]);
}
void init(int n) {
for(int i = 0; i < n + 5; i++) {
ex[i] = ex2[i] = 1;
dp[i] = maxx[i] = sum[i] = 0;
dp2[i] = maxx2[i] = 0;
}
num.clear();
num2.clear();
}
int main() {
int t;
scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &k);
init(n);
for(int i = 1; i <= n; i++)
scanf("%d", a + i);
solve1();
for(int i = 1; i <= min(n, k); i++)
if(ex[i] == 1 && maxx[i] <= k)
dp[i] = 1;
dp[0] = 1;
for(int i = min(n, k) + 1; i <= n; i++) {
int f = ex[i];
if(i - k >= 0) {
if(dp[i - k] == 0)
f = 0;
if((sum[i] - sum[i - k]) != 1ll * k * (k + 1) / 2)
f = 0;
}
if(f)
dp[i] = 1;
}
solve2();
dp2[n + 1] = 1;
for(int i = n; i >= 1 && n - i + 1 <= k; i--)
if(ex2[i] == 1 && maxx2[i] <= k)
dp2[i] = 1;
int ans = 0;
for(int i = 1; i <= n; i++)
if(dp[i] && dp2[i + 1])
ans = 1;
if(ans)
printf("YES\n");
else
printf("NO\n");
}
}