zoukankan      html  css  js  c++  java
  • 《Educational Codeforces Round 111 (Rated for Div. 2)》

    A:dp处理一下:dp[i][j] - 以j结尾,和为i的最小长度。

    转移很简单。

    // Author: levil
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<LL,int> pii;
    const int N = 1e3 + 5;
    const int M = 2e3 + 5;
    const LL Mod = 4294967296;
    #define pi acos(-1)
    #define INF 1e9
    #define dbg(ax) cout << "now this num is " << ax << endl;
    
    int dp[5005][5005];//dp[i][j] - 以j结尾,和为i的最小长度
    void init() {
        memset(dp,0x3f3f3f,sizeof(dp));
        dp[1][1] = 1;
        for(int i = 2;i <= 5000;++i) {
            for(int j = 1;j <= 5000;++j) {
                if(i - j >= 0) dp[i][j] = min(dp[i][j],dp[i - j][j] + 1);
                if(i - j >= 0) dp[i][j] = min(dp[i][j],dp[i - j][j - 1] + 1);
                if(i - j >= 0 && j - 2 >= 0) dp[i][j] = min(dp[i][j],dp[i - j][j - 2] + 1);
            }
        }
    }
    int main() {
        init();
        int ca;scanf("%d",&ca);
        while(ca--) {
            int n;scanf("%d",&n);
            int mi = INF;
            for(int i = 1;i <= 5000;++i) mi = min(mi,dp[n][i]);
            printf("%d
    ",mi);
        }
    
       // system("pause");
        return 0;
    }
    View Code

    B:可以发现,a的贡献是固定的即n * L.

    这里对答案有影响的就是b的贡献。

    我们对连续的一段缩点,可以发现,如果b >= 0,我们要最大化b的代价就是一个个删除。

    b < 0,就需要一段段去删,去奇数对称和偶数对称讨论之后可以发现贡献都是一样的。

    // Author: levil
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<LL,int> pii;
    const int N = 2e5 + 5;
    const int M = 2e3 + 5;
    const LL Mod = 4294967296;
    #define pi acos(-1)
    #define INF 1e9
    #define dbg(ax) cout << "now this num is " << ax << endl;
    
    int main() {
        int ca;scanf("%d",&ca);
        while(ca--) {
            int n,a,b;scanf("%d %d %d",&n,&a,&b);
            string s;cin >> s;
            int cnt = 1;
            char c = s[0];
            for(int i = 1;i < s.size();++i) {
                if(s[i] == c) continue;
                else {
                    ++cnt;
                    c = s[i];
                }
            }
            LL sum = n * a;
            if(b >= 0) sum += n * b;
            else sum += (cnt / 2 + 1) * b;
            printf("%lld
    ",sum);
        }
    
    
    
     //   system("pause");
        return 0;
    }
    View Code

    C:从图形上看,对于递增的三个x坐标,i,j,k。

    如果a[j] 在 a[i] 和 a[k]围成的长方形里,肯定就满足这个情况,因为横纵距离都刚好是相加。

    并且除了这种情况之外,别的情况都无法再形成。

    即两种情况。a[i] <= a[j] <= a[k],或者 a[i] >= a[j] >= a[k]。

    我们单调栈维护一下左右最近的 <= 和 >= 位置。

    但是这里要算贡献,还不行,我们可以发现,我们只是处理除了每段最小的坏区间。

    还有很多的搭配没被计算到。

    这里我们有一种思路就是如果坏区间是[L,r]。那么我们就更新区间[1,L]里的mi = r。一直维护这个最小值。

    这点可以用线段树做到,这样我们最后就可以计算以每个i点为左端点的区间合法区间数量。复杂度(O(nlogn)

    但是我们也可以从右往左维护合法区间。就是不断缩小不合法区间的右端点,并且因为从右往左,这个区间肯定包含在当前里且 >= 当前的坐标。

    // Author: levil
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<LL,int> pii;
    const int N = 2e5 + 5;
    const int M = 2e3 + 5;
    const LL Mod = 4294967296;
    #define pi acos(-1)
    #define INF 1e9
    #define dbg(ax) cout << "now this num is " << ax << endl;
    
    int a[N],L[N],r[N],Q[N],S[N],mi[N],le[N],ri[N];
    int main() {
        int ca;scanf("%d",&ca);
        while(ca--) {
            int n;scanf("%d",&n);
            for(int i = 1;i <= n;++i) scanf("%d",&a[i]),L[i] = r[i] = le[i] = ri[i] = 0,mi[i] = INF;
            int top = 0,tot = 0;
            Q[++top] = 1;
            S[++tot] = 1;
            for(int i = 2;i <= n;++i) {
                while(top != 0 && a[i] < a[Q[top]]) --top;
                if(top != 0) L[i] = Q[top]; 
                Q[++top] = i;
                while(tot != 0 && a[i] > a[S[tot]]) --tot;
                if(tot != 0) le[i] = S[tot];
                S[++tot] = i;
            }
            top = tot = 0;
            Q[++top] = n;
            S[++tot] = n;
            for(int i = n - 1;i >= 1;--i) {
                while(top != 0 && a[i] > a[Q[top]]) --top;
                if(top != 0) r[i] = Q[top];
                Q[++top] = i;
                while(tot != 0 && a[i] < a[S[tot]]) --tot;
                if(tot != 0) ri[i] = S[tot];
                S[++tot] = i;
            }
            for(int i = 1;i <= n;++i) {
                if(L[i] == 0 || r[i] == 0) continue;
                mi[L[i]] = min(mi[L[i]],r[i]); 
            }
            for(int i = 1;i <= n;++i) {
                if(le[i] == 0 || ri[i] == 0) continue;
                mi[le[i]] = min(mi[le[i]],ri[i]);
            }
            LL ans = 0;
            int pre = n + 1;
            for(int i = n;i >= 1;--i) {
                pre = min(pre,mi[i]);
                ans += (pre - i);
            }
            printf("%lld
    ",ans);
        }
    
    
    
        //system("pause");
        return 0;
    }
    View Code

    D:我们对题目中的式子转化一下,假设有一个数组b,满足bi + i = ai。

    那么ai + aj = bi + i + bj + j = i + j 即 bi = -bj。

    也就是对于数组b,这两个数的值相反。为了最大化这个组数,我们对半分配一样的值k,一半b = k,一半b = -k,对于奇数个时,有两种情况:

    1:n / 2 = k,n / 2 + 1 = -k.

    2: n / 2 + 1 = k,n / 2 = -k;

    然后我们考虑怎么去分配这个值,对于区间[L,r]。

    首先,我们可以发现,对于每个a的值都是i - k或者i + k,这个值显然不满足ai = i,所以第一个条件恒成立。

    我们可以把区间分成三段[1,x],[x,y],[y,n]。

    当满足1 - k >= L && n + k <= r 即k <= min(1 - L,r - n)时,对于每个值都可以取正和负。

    那么对于每个k就有C(n,n / 2)种方案。

    当k > min(1 - L,- n)时,假设lf = max(1,L + k),rg = min(n,r - k).

    对于[1,lf)的,显然只能取i + k,对于(rg,n],显然只能取i - k,然后再对中间的都可以取。

    // Author: levil
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<LL,int> pii;
    const int N = 2e5 + 5;
    const int M = 2e3 + 5;
    const LL Mod = 1e9 + 7;
    #define pi acos(-1)
    #define INF 1e9
    #define dbg(ax) cout << "now this num is " << ax << endl;
    
    LL f[N],inv[N];
    LL quick_mi(LL a,LL b) {
        LL re = 1;
        while(b) {
            if(b & 1) re = re * a % Mod;
            a = a * a % Mod;
            b >>= 1;
        }
        return re;
    }
    void init() {
        f[0] = 1;
        for(int i = 1;i < N;++i) f[i] = f[i - 1] * i % Mod;
        inv[N - 1] = quick_mi(f[N - 1],Mod - 2);
        for(int i = N - 2;i >= 0;--i) inv[i] = inv[i + 1] * (i + 1) % Mod;
    }
    LL C(LL n,LL m) {
        if(n < m || m < 0) return 0;
        return f[n] * inv[n - m] % Mod * inv[m] % Mod;
    }
    int main() {
        init();
        int ca;scanf("%d",&ca);
        while(ca--) {
            int n,L,r;scanf("%d %d %d",&n,&L,&r);
            int limit = min(1 - L,r - n);
            LL ans = C(n,n / 2) * limit % Mod;
            if(n % 2 == 0) {
                for(int k = limit + 1;;++k) {
                    int lf = max(1,L + k),rg = min(n,r - k);
                    if(rg - lf + 1 < 0) break;
                    ans = (ans + C(rg - lf + 1,n / 2 - lf + 1)) % Mod;
                }
            }
            else {
                ans = (ans + C(n,n / 2 + 1) * limit % Mod) % Mod;
                for(int k = limit + 1;;++k) {
                    int lf = max(1,L + k),rg = min(n,r - k);
                    if(rg - lf + 1 < 0) break;
                    if(n / 2 - lf + 1 >= 0) ans = (ans + C(rg - lf + 1,n / 2 - lf + 1)) % Mod;
                    if(n / 2 + 1 - lf + 1 >= 0) ans = (ans + C(rg - lf + 1,n / 2 + 1 - lf + 1)) % Mod;
                }
            }
            printf("%lld
    ",ans);
        }
    
    
        //system("pause");
        return 0;
    }
    View Code

    E:

    二分答案.

    这里唯一的标准就是我们每个块在字符串中的顺序是固定的,所以我们可以状压块进入的顺序。

    对于一个子集s,如果它不包含块c,那么就说明s | c,就代表s后面接的块是c,那么我们就需要预处理出pos[i][j]表示i位置后面块j结尾的最小位置。

    因为我们用最小的取维护肯定是最优的,对于这个位置的寻找,官方题解里用了一个非常巧妙的正反迭代,对于现在的长度是d,i + d,里面没有别的字符的位置,那就说明这段可以变成连续的j。

    // Author: levil
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<LL,int> pii;
    const int N = 2e5 + 5;
    const int M = 2e3 + 5;
    const LL Mod = 1e9 + 7;
    #define pi acos(-1)
    #define INF 1e9
    #define dbg(ax) cout << "now this num is " << ax << endl;
    
    
    string s;
    int n,k,lst[20],dp[1 << 18],pos[N][20];
    bool check(int x) {
        fill(pos[0],pos[0] + N * 20,n + 1);
        fill(lst,lst + 20,n + 1);
        fill(dp,dp + (1 << 18),n + 1);
        //printf("x is %d
    ",x);
        for(int i = n;i >= 1;--i) {
            if(s[i - 1] != '?') lst[s[i - 1] - 'a'] = i;
            int mix = n + 1;
            for(int j = 0;j < k;++j) {
                int nxt = i + x - 1;
                if(nxt < mix) pos[i][j] = nxt;
                else pos[i][j] = pos[i + 1][j];
                mix = min(mix,lst[j]);
            }
            mix = n + 1;
            for(int j = k - 1;j >= 0;--j) {
                int nxt = i + x - 1;
                if(nxt >= mix) pos[i][j] = pos[i + 1][j];
                mix = min(mix,lst[j]);
            }
            //for(int j = 0;j < k;++j) printf("dp[%d][%d] is %d
    ",i,j,pos[i][j]);
        }
        dp[0] = 0;
        for(int i = 0;i < (1 << k);++i) {
            for(int j = 0;j < k;++j) {
                int g = ((i >> j) & 1);
                if(g == 1) continue;
                if(dp[i] <  pos[dp[i] + 1][j]) {
                    dp[i | (1 << j)] = min(dp[i | (1 << j)],pos[dp[i] + 1][j]);
                }
            }
        }
        return dp[(1 << k) - 1] < n + 1;
    }
    int main() {
        scanf("%d %d",&n,&k);
        cin >> s;
        int L = 1,r = n,ans = 0;
        while(L <= r) {
            int mid = (L + r) >> 1;
            if(check(mid)) L = mid + 1,ans = mid;
            else r = mid - 1;
        }
        printf("%d
    ",ans);
    
       // system("pause");
        return 0;
    }
    View Code
  • 相关阅读:
    59
    58
    57
    56
    55
    54
    53
    转 Using $.ajaxPrefilter() To Configure AJAX Requests In jQuery 1.5
    jquery用正则表达式验证密码强度
    什么是高内聚、低耦合?(转载)
  • 原文地址:https://www.cnblogs.com/zwjzwj/p/15026863.html
Copyright © 2011-2022 走看看