zoukankan      html  css  js  c++  java
  • 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) 时,第 (j) 列的贡献为 (sum[i][j]/a[i][j]) ,按贡献大小 sort 一下,依次选择即可。

    复杂度为 (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
    ", 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
    ");
            else
                printf("NO
    ");
        }
    }
    
  • 相关阅读:
    configparser模块
    xml文件解析
    shutil模块 + shelve模块 二合一版
    hashlib模块
    subprocess模块和sys模块
    re模块
    os模块
    random模块
    time模块、datetime模块讲解
    洛谷P3414 SAC#1
  • 原文地址:https://www.cnblogs.com/Hartley/p/13389700.html
Copyright © 2011-2022 走看看