Codeforces Round #562 (Div. 1)

Codeforces Round #562 (Div. 1)

A:
題意是給定一個數組,每次可以對數組中的任意一個子集中的元素自增1,求最少需要多少次操作使得這個數組非降序。
顯然可以看出操作次數對這個問題滿足二分性質的,二分一下就行了。

#include <bits/stdc++.h>
using namespace std;
const int N = 3e5+7;
int a[N];
int n, m;
bool check(int k) {
    int p;
    if(a[1]+k>=m) p=0;
    else p=a[1];
    for(int i=2; i<=n; ++i) {
//        cout << p << endl;
        if(a[i]+k<p) return false;
        if(a[i]+k>=m&&(a[i]+k)%m>=p) continue;
        if(a[i]<=p) continue;
        else p=a[i];
    }
    return true;
}
int main() {
    scanf("%d %d", &n, &m);
    for(int i=1; i<=n; ++i) scanf("%d", &a[i]);
    int l=0,r=m-1;
//    check(0);
//    exit(0);
    while(l<r) {
        int mid=(l+r)/2;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    printf("%d\n", r);
}

B:
題意是 sx,sx+k,sx+2ks_{x}, s_{x+k}, s_{x+2k} 的這樣的三元組,如果三個數都相等,那麼就是合法的三元組。
求有多少對 [l,r][l,r] 使得在 [l,r][l,r] 上有這樣的合法三元組。
可以用bitset對 kk 小於 10410^4 的情況算出來,進行剪枝,剩下的部分暴力計算。隨機情況下剩下的部分是個位數,因此複雜度大概就是bitset這部分。

#include <bits/stdc++.h>
using namespace std;
const int N = 3e5+7;
const int B = 1e4;
bitset<N> b;
bitset<N> p;
char s[N];
int f[N], n;
//int c = 0;
void cal() {
    for(int i=0; i<n; ++i) p.set(i);
    for(int i=1; i<=min(n/2, B); ++i) {
        bitset<N> t = p&b&(b>>i)&(b>>(i*2));
//        if(i==2) cout << t._Find_first() << " " << t.test(0) << endl;
        for(int j=t._Find_first(); j<n; j=t._Find_next(j)) {
//            printf("f[%d]=%d\n",j, i);
//            ++c;
            f[j]=i;
        }
        p &= t.flip();
    }
    bitset<N> t = p&b;
    vector<int> a;
    for(int j=t._Find_first(); j<n; j=t._Find_next(j)) {
        a.push_back(j);
//        printf("%d\n", j);
    }
    if(n/2<=B||a.empty()) return;
    for(int i=0; i<a.size(); ++i) {
        int v = a[i];
        for(int j=B+1; v+2*j<n; ++j) {
            int l=v+j, r=v+2*j;
            if(b.test(l)&&b.test(r)) {
                f[a[i]]=j;
//                printf("f[%d]=%d\n",j, i);
                break;
            }
        }
    }
}
int main() {
    scanf("%s", s);
    n =strlen(s);
    for(int i=0; i<n; ++i) {
        if(s[i]=='1') b.set(i);
        f[i]=n;
    }
    cal();
    for(int i=0; i<n; ++i) {
        b.flip(i);
    }
    cal();
    int mn = n;
    long long ans=0;
//    for(int i=0;i<n;++i) printf("%d\n", f[i]);
    for(int i=n-1; i>=0; --i) {
        mn=min(mn, i+f[i]*2);
        ans += n-mn;
    }
//    printf("%d\n", c);
    printf("%I64d\n", ans);
}

C:
題意是對於每個詢問(x,y)(x,y) ,求出是否存在一個子序列,這個子序列提取出來後,第一個數是 a[x]a[x] , 最後一個數是a[y]a[y],且子序列相鄰兩個數按位與都大於0 ,這種情況稱爲 xxyy 可達。
dp[i][j]=kdp[i][j]=k 爲第一個 kik \geq ia[k]a[k] 中位 jj11 的數的下標。這樣對於一個詢問 (x,y)(x, y) 就可以枚舉 yy 被置1的所有位 jj ,求 dp[x][j]dp[x][j] 的最小值,看是否小於等於 yy 即可。
dp[i][j]dp[i][j] 的計算方法是對於所有 a[i]a[i] 被置1的位,枚舉同樣被置1的右邊第一個數 kkdp[k][j]dp[k][j] 的最小值等於 dp[i][j]dp[i][j] 了。
本題的關鍵是能想出這樣表示 dp[i][j]dp[i][j] 的話可以分別表示到 xx 可達和到 yy 可達。

#include <bits/stdc++.h>
using namespace std;
const int N = 3e5+7;
const int B = 19;
int last[B];
int dp[N][B];
// dp[i][j] 表示大於等於i的第一個可達i且位j被置位的點的下標。
int a[N];
int main() {
    int n, q;
    scanf("%d%d", &n, &q);
    for(int i=1; i<=n; ++i) scanf("%d", &a[i]);
    for(int i=0; i<B; ++i) last[i] = N;
    for(int i=n; i>=1; --i) {
        int k = a[i];
        for(int j=0; j<B; ++j) {
            if((k>>j)&1) {
                dp[i][j] = i;
            } else {
                dp[i][j] = N;
                for(int p=0; p<B; ++p) {
                    if((k>>p)&1&&last[p]!=N) {
                        dp[i][j] = min(dp[i][j], dp[last[p]][j]);
                    }
                }
            }
        }
        for(int j=0; j<B; ++j) {
            if((k>>j)&1) last[j]=i;
        }
    }
    while(q--) {
        int x, y;
        scanf("%d%d", &x, &y);
        int ans = N;
        for(int i=0; i<B; ++i) {
            if((a[y]>>i)&1) {
                ans = min(ans, dp[x][i]);
            }
        }
        if(ans<=y) puts("Shi");
        else puts("Fou");
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章