《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\n",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\n",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\n",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\n",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\n",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\n",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\n",ans);

   // system("pause");
    return 0;
}
View Code

 

posted @ 2021-07-18 16:19  levill  阅读(40)  评论(0编辑  收藏  举报