Codeforces Round #604 (Div. 2)


A. Beautiful String (CF 1265 A)

題目大意

當沒有連續兩個字母相同時,該字符串為美麗串,給定一個含\(a,b,c,?\)的字符串,要求將\(?\)替換成\(a\),\(b\)\(c\),使得該串為美麗串。若無法成為美麗串輸出\(-1\)

解題思路

很顯然對於每個\(?\)的取值只跟它左右兩個位置有關,而它一定能取到一個合法的值,只要跟左右不同即可,而如果一開始就有連續兩個字母相同則輸出\(-1\).

神奇的代碼
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

bool qwq;

char s[100500];

void check(int x){
    s[x]='a';
    if (x==0){
        if (s[x+1]=='a') s[x]='b';
    }
    else{
        if (s[x-1]=='a'||s[x+1]=='a') {
                                        s[x]='b';
                                        if (s[x-1]=='b'||s[x+1]=='b') {
                                                                        s[x]='c';
                                                                        if (s[x-1]=='c'||s[x+1]=='c') qwq=false;
                                        }
        }
         
    }
}

void Input(void) {
    scanf("%s",s);
    int len=strlen(s);
    qwq=true;
    for(int i=0;i<len;++i){
        if (qwq==false) break;
        if (i!=0&&s[i]==s[i-1]) qwq=false;
        if (s[i]=='?') check(i);
    }
    if (!qwq) printf("-1\n");
    else printf("%s\n",s); 
}

void Solve(void) {}

void Output(void) {}

main(void) {
    int kase;
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    read(kase);
    for (int i = 1; i <= kase; i++) {
        //printf("Case #%d: ", i);
        Input();
        Solve();
        Output();
    }
}


B. Beautiful Numbers (CF 1265 B)

題目大意

給定一個排列,求每一個\(m\leq n\),是否存在\(l,r\)使得區間\(\left[ l,r\right]\)\(1\)~\(m\)的一個全排列,存在則\(m\)為美麗數字。輸出一個數字串,第\(i\)個數字表示\(i\)是否是美麗數字,是則\(1\),否則\(0\)

解題思路

\(m=1\)的時候我們選擇\(1\)的位置,然后我們從\(1\)的位置進行拓展,很顯然我們每一次貪心的向左右兩個數中較小的那個數進行擴展,才有可能得到一個\(1\)~\(m\)的全排列。

神奇的代碼
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

int n,l,r,pos,cnt,mi,ma;

const int N=2e5+8;

int p[N];

void Input(void) {
    read(n);
    for(int i=1;i<=n;++i) {
        read(p[i]);
        if (p[i]==1) pos=i;
    }
}

void Solve(void) {
    p[0]=p[n+1]=3e6;
    l=r=pos;
    cnt=1;
    mi=ma=1;
    putchar('1');
    while(cnt<n){
        if (l==1){
            ++r;
            ++cnt;
            ma=MAX(ma,p[r]);
        }else if (r==n){
            --l;
            ++cnt;
            ma=MAX(ma,p[l]);
        }else if (p[l-1]>p[r+1]) {
            ++r;
            ++cnt;
            ma=MAX(ma,p[r]);
        }
        else{
            --l;
            ++cnt;
            ma=MAX(ma,p[l]);
        }
        if (cnt==ma-mi+1) putchar('1'); 
        else putchar('0');
    }
    puts("");
}

void Output(void) {}

main(void) {
    int kase;
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    read(kase);
    for (int i = 1; i <= kase; i++) {
        //printf("Case #%d: ", i);
        Input();
        Solve();
        Output();
    }
}


C. Beautiful Regional Contest (CF 1265 C)

題目大意

\(n\)個選手參加比賽,遞減給出它們解決的題數,分配\(g\)個金牌\(s\)個銀牌和\(b\)個銅牌,要求\(g<s\)\(g<b\),且\(g+s+b\leq \dfrac {n}{2}\),且獲得金牌的選手的題數嚴格大於獲得銀牌選手的題數,獲得銀牌的選手題數嚴格大於獲得銅牌選手的題數,獲得銅牌的選手題數嚴格大於獲得鐵牌(無牌)(來杯拿鐵咖啡)選手的題數,問能否做到,若能,給定一個可行的分配方案,使得發牌數最大。

解題思路

題目對於\(g\)有大小限制而\(s\)\(b\)之間沒有,則我們讓\(g\)盡可能小就好了,即選解題數最高的那一組選手全部發金牌,然后發銀牌,直到\(g>s\),剩下的選手全部發銅牌,直到發的牌數恰好小於\(\dfrac {n}{2}\)即可,做不到則不可行。
如果你是良心比賽方可以最后把\(s\)\(b\)調換一下。

神奇的代碼
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

int n,g,s,b,qwq,re;

const int N=4e5+8;

int num[N],cnt[N];

void Input(void) {
    qwq=0;
    num[0]=-1;
    read(n);
    for(int a,i=1;i<=n;++i) {
        read(a);
        if (num[qwq]!=a) num[++qwq]=a;
        ++cnt[qwq];
    }
    s=g=b=re=0;
}

void Solve(void) {
    g=cnt[1];
    int i=2;
    while(s<=g) s+=cnt[i++];
    while(b<=g) b+=cnt[i++];
    if (s+g+b>n/2) g=s=b=0;
    else {while(s+g+b<=n/2) b+=cnt[i++]; b-=cnt[--i];}
    printf("%d %d %d\n",g,s,b);
    for(int i=1;i<=n;++i) num[i]=cnt[i]=0;
}

void Output(void) {}

main(void) {
    int kase;
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    read(kase);
    for (int i = 1; i <= kase; i++) {
        //printf("Case #%d: ", i);
        Input();
        Solve();
        Output();
    }
}


D. Beautiful Sequence (CF 1265 D)

題目大意

給了\(a\)\(0\)\(b\)\(1\)\(c\)\(2\)\(d\)\(3\),要求排成一個序列,使得倆倆差值為\(1\),輸出任意符合要求序列即可,沒有則\(NO\)

解題思路

  • 如果有三個數是\(0\)個,則第四個數數量為\(1\)則可行,否則不可行。
  • 如果有兩個數是\(0\)個,則看另外兩個數的差值是否為\(1\),是則由植樹原理再判斷二者的差值是否為一,是則可行,其余情況均不可行。
  • 如果有一個數是\(0\)個,則看這個數是多少,若為\(1\)\(2\)則不可行,當為\(0\)\(3\)時,不失一般性,假設是\(0\)的數為\(0\)個,那么我們排列\(1\),\(2\),\(3\),由植樹原理可知如果\(cnt=c-\left( b-1+d-1+1\right) \leq 2\)則可行,即\(1,2,1,2,1,2......,2,1, 2, 3,2,3,2......2,3,\)如此排列,若\(cnt\)\(1\)則在序列最左邊放一個\(2\),若為\(2\)則在最左邊和最右邊各放一個\(2\)即可。
  • 如果有零個數是\(0\)個,我們考慮\(0,1\)\(2,3\),首先\(b\geq a\)\(c\geq d\),否則不可行。我們按照,\(0,1,0,1......\)\(......2,3,2,3\)如此排好后,考慮\(1\)\(2\)之間的排列,我們如此\(2,1,2,1.....\)排列,直到用光\(1\)或者\(2\),此時如果還有剩余的,假設\(1\)還有剩余,若剩余\(1\)個,則放到最左邊,否則不可行,若\(2\)還有剩余,若剩余\(1\)個,則放到最右邊,否則不可行.

綜上即可。

神奇的代碼
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

int a,b,c,d,mi,cnt;

bool qwq;

void Input(void) {
    cnt=0;
    read(a);
    read(b);
    read(c);
    read(d);
    cnt=(a==0)+(b==0)+(c==0)+(d==0);
}

void check1(){
    int sign=0;
    if (a==0) sign=0;
    else if (b==0) sign=1;
    else if (c==0) sign=2;
    else sign=3;
    if (sign==1||sign==2){
        qwq=true;
        return;
    }
    if (sign==3){
        b-=a-1;
        b-=c-1;
        --b;
        if (b<0||b>2){
            qwq=true;
            return;
        }
        else{
            printf("YES\n");
            if (b) --b,printf("1 ");
            for(int i=1;i<=a;++i) printf("0 1 ");
            for(int i=1;i<c;++i) printf("2 1 ");
            printf("2");
            if (b) printf(" 1\n");
            else printf("\n");
            return;
        }
    }
    else{
        c-=b-1;
        c-=d-1;
        --c;
        if (c<0||c>2){
            qwq=true;
            return;
        }
        else{
            printf("YES\n");
            if (c) --c,printf("2 ");
            for(int i=1;i<=b;++i) printf("1 2 ");
            for(int i=1;i<d;++i) printf("3 2 ");
            printf("3");
            if (c) printf(" 2\n");
            else printf("\n");
            return;
        }
    }
}

void check2(){
    int s1=-1,s2=-1;
    int cnt[4];
    cnt[0]=a;
    cnt[1]=b;
    cnt[2]=c;
    cnt[3]=d;
    if (a!=0) s1=0;
    if (b!=0) if (s1==-1) s1=1;else s2=1;
    if (c!=0) if (s1==-1) s1=2;else s2=2;
    if (d!=0) if (s1==-1) s1=3;else s2=3;
    if (s2-s1>1) {
        qwq=true;
        return;
    }
    else{
        if (ABS(cnt[s2]-cnt[s1])>1){
            qwq=true;
            return;
        }
        else{
            printf("YES\n");
            if (cnt[s2]>cnt[s1]) {for(int i=1;i<=cnt[s1];++i) printf("%d %d ",s2,s1); printf("%d\n",s2);}
            else if (cnt[s2]<cnt[s1]) {for(int i=1;i<=cnt[s2];++i) printf("%d %d ",s1,s2); printf("%d\n",s1);}
            else for(int i=1;i<=cnt[s1];++i) printf("%d %d%c",s1,s2,i==cnt[s1]?'\n':' ');
            return;
        }
    }
}

void check3(){
    int si=0;
    int cnt[4];
    cnt[0]=a;
    cnt[1]=b;
    cnt[2]=c;
    cnt[3]=d;
    for(int i=0;i<4;++i) if (cnt[i]!=0) si=i;
    if (cnt[si]==1){
        printf("YES\n");
        printf("%d\n",si);
        return;
    }
    else {
        qwq=true;
        return;
    }
}
void Solve(void) {
    qwq=false;
    if (a>b&&(c!=0||d!=0)) qwq=true;
    else if (d>c&&(b!=0||a!=0)) qwq=true;
    if (!qwq){
        if (cnt==0){
        b-=a;
        c-=d;
        mi=MIN(b,c);
        b-=mi;
        c-=mi;
        if (b==1||c==1){
            printf("YES\n");
            if (b==1) {
                printf("1 ");
                for(int i=1;i<=a;++i) printf("0 1 ");
                for(int i=1;i<=mi;++i) printf("2 1 ");
                for(int i=1;i<=d;++i) printf("2 3%c",i==d?'\n':' ');
            }
            else{
                for(int i=1;i<=a;++i) printf("0 1 ");
                for(int i=1;i<=mi;++i) printf("2 1 ");
                for(int i=1;i<=d;++i) printf("2 3 "); 
                printf("2\n");
            }
        }
        else if (b==c){
                printf("YES\n");
                for(int i=1;i<=a;++i) printf("0 1 ");
                for(int i=1;i<=mi;++i) printf("2 1 ");
                for(int i=1;i<=d;++i) printf("2 3%c",i==d?'\n':' '); 
        }
        else qwq=true;
        }
        else{
            if (cnt==1) check1();
            else if (cnt==2) check2();
            else if (cnt==3) check3();
        }
    }
    if (qwq) printf("NO\n");
}

void Output(void) {}

main(void) {
    //int kase;
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    //read(kase);
    //for (int i = 1; i <= kase; i++) {
        //printf("Case #%d: ", i);
        Input();
        Solve();
        Output();
    //}
}

由於昨晚放棄思考了所以代碼寫了巨長……

E. Beautiful Mirrors (CF 1265 E)

題目大意

\(n\)枚鏡子,每天問一枚鏡子,從第一枚鏡子開始問\(Creatnx\)是否漂亮,是則下一天跳轉到下一個鏡子,否則下一天從第一枚鏡子重新開始問。當最后一枚鏡子即第\(n\)枚鏡子說她漂亮時,她就變得很開心。問她變得開心的期望天數。

解題思路

期望題,我們通常逆推,即從結果推到初始狀態。
\(f\left( i\right)\)表示從第\(i\)枚鏡子開始問,變得開心(即問到第n枚鏡子得到漂亮回答)的期望天數。
根據期望的定義,我們有\(f\left( n+1\right) =0\)以及\(f\left( i\right) =\dfrac {p_{i}}{100}f\left( i+1\right) +\left( 1-\dfrac {p_{i}}{100}\right) f\left( 1\right)\)
即對於第\(i\)天,它有\(\dfrac {p_{i}}{100}\)的概率需要\(f\left( i+1\right)\)才變得開心,也有\(\left( 1-\dfrac {p_{i}}{100}\right)\)的概率需要\(f\left( 1\right)\)才變得開心。
右式一共有\(n\)個方程,我們可以用高斯消元即可解出答案\(f\left( 1\right)\),但復雜度是\(O\left( n^{3}\right)\),會超時。
由於逆推狀態不當,導致式子中存在\(f\left( 1\right)\),這對答案求值造成很大不便,我們需要修改狀態。
由於回答不漂亮會回到第一枚鏡子,那么我們希望第一枚鏡子的狀態會是一個已知狀態。
我們設\(f\left( i\right)\)表示從第一枚鏡子問,問到第\(i\)枚鏡子且回答漂亮的期望天數。
\(f\left( 0\right) =0\)\(f\left( i\right) =f\left( i-1\right) +\dfrac {p_{i}}{100}+\left( 1-\dfrac {p_{i}}{100}\right) \left( f\left( i\right) +1\right)\)
即我們要問第\(i\)枚鏡子,首先需要期望天數\(f\left( i-1\right)\)問到第\(i-1\)枚鏡子並得到漂亮的回答,然后對於第\(i\)枚鏡子,我們有\(\dfrac {p_{i}}{100}\)的概率花一天得到漂亮的回答,也有\(\left( 1-\dfrac {p_{i}}{100}\right)\)的概率得到不漂亮回答,這時需要再花\(f\left( i\right)\)天才能得到漂亮的回答,所以總共需要\(f\left( i\right) +1\)\(1\)是問第\(i\)枚鏡子得到不漂亮的回答的那一天),化簡一下即可得到\(f\left( i\right) =\dfrac {\left( f\left( i-1\right) +1\right) \times 100}{p_{i}}\),遞推\(O\left( n\right)\)即可得到答案\(f\left( n\right)\)
(\(f\left( i\right) =\dfrac {p_{i}}{100}\cdot \left( f\left( i-1\right) +1\right) +\left( 1-\dfrac {p_{i}}{100}\right) \cdot \left( f\left( i-1\right) +1+f\left( i\right) \right)\)這個和上面是等價的,即有\(\dfrac {p_{i}}{100}\)的概率得到漂亮答案,這時需要的天數是\(f\left( i-1\right) +1\),也有\(\left( 1-\dfrac {p_{i}}{100}\right)\)的概率得到了不漂亮回答,這時我們需要從第一枚鏡子重新問,此時得到第\(i\)枚鏡子的漂亮回答的天數是\(\left( f\left( i-1\right) +1+f\left( i\right) \right)\))

神奇的代碼
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

const long long mo=998244353;

long long kuai(long long a,long long b){
    int qwq=1;
    while(b){
        if (b&1) qwq=a*qwq%mo;
        a=a*a%mo;
        b>>=1;
    }
    return qwq;
}

void Input(void) {
    int n;
    long long ans=0,f;
    read(n);
    for(int i=1;i<=n;++i){
        read(f);
        ans=(ans+1)%mo*100%mo*kuai(f,mo-2)%mo;
    }
    printf("%lld\n",ans);
}

void Solve(void) {}

void Output(void) {}

main(void) {
    //int kase;
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    //read(kase);
    //for (int i = 1; i <= kase; i++) {
        //printf("Case #%d: ", i);
        Input();
        Solve();
        Output();
    //}
}


C. Beautiful Mirrors with queries (CF 1264 C)

題目大意

\(n\)枚鏡子,每天問一枚鏡子,從第一枚鏡子開始問\(Creatnx\)是否漂亮,是則下一天跳轉到下一個鏡子,否則下一天從小於當前鏡子編號的標有\(check point\)的最大編號的鏡子問。當最后一枚鏡子即第\(n\)枚鏡子說她漂亮時,她就變得很開心。
\(q\)次操作,每次操作將第\(i\)枚鏡子設為\(check point\)(如果之前不是\(check point\))或者取消\(check point\)(如果之前是\(check point\)),對於每次操作,回答她變得開心的期望天數。

解題思路

由於鏡子回答不漂亮的時候\(Creatnx\)不再是從第一枚鏡子問,\(check point\)相當於把連續的鏡子分成了若干段,不同段之間互不干擾,對於每一段\(\left[ l,r\right]\),我們都可以假想是從第一枚鏡子問到第\(r-l+1\)枚鏡子並得到漂亮回答的期望天數,由期望的線性可加性,我們把不同段的期望天數相加即可得到答案。而每次操作僅僅增加或移除一個\(check point\),如果我們能夠快速知道某段區間對答案貢獻的期望天數,即可快速解決此題。

上一題中一開始的遞推式\(f\left( i\right) =\dfrac {p_{i}}{100}f\left( i+1\right) +\left( 1-\dfrac {p_{i}}{100}\right) f\left( 1\right)\),某人通過解前幾項的規律得出$$f\left( 1\right) = \frac{1 + p_1 + p_1 \cdot p_2 + \ldots + p_1\cdot p_2 \cdot \ldots \cdot p_{n-1}}{p_1\cdot p_2 \cdot \ldots \cdot p_n}$$,這是我們從\(1\)號鏡子開始問問到第\(n\)號鏡子並得到漂亮回答的答案。其實這個從后來的遞推式\(f\left( i\right) =\dfrac {\left( f\left( i-1\right) +1\right)}{p_{i}}\)展開也可得到。

這樣子的話,對於一個新添加的\(check point\),記為\(mid\),再設編號小於\(check point\)編號的最大\(check point\)編號為\(l\),編號大於\(check point\)編號的最小\(check point\)編號為\(r\),則從\(1\)\(l-1\)的期望天數,從\(r\)\(n\)的期望天數並沒有受到影響,而從\(l\)\(r-1\)的期望天數被分成了兩部分,一部分是從\(l\)\(mid-1\),一部分是\(mid\)\(r-1\),我們只要減去\(l\)\(r-1\)的期望貢獻,再加上\(l\)\(mid-1\)\(mid\)\(r-1\)的期望貢獻即可得到新的答案,移除的話即減去\(l\)\(mid-1\)\(mid\)\(r-1\)的期望貢獻,加回\(l\)\(r-1\)的期望貢獻即可。

從上面展開遞推式的過程(這里沒有)我們可以知道,對於從\(u\)\(v\)都得到漂亮回答的期望天數為$$\frac{1 + p_u + p_u \cdot p_{u+1} + \ldots + p_u \cdot p_{u+1} \cdot \ldots \cdot p_{v-1}}{p_u \cdot p_{u+1} \cdot \ldots \cdot p_v} = \frac{A}{B}$$,由此我們只要預處理\(s_i = p_1 \cdot p_2 \cdot \ldots \cdot p_i\)以及\(t_i = p_1 + p_1 \cdot p_2 + \ldots + p_1 \cdot p_2 \ldots \cdot p_i\),那么對於從\(u\)\(v\)都得到漂亮回答的情況,\(A = \frac{t_{v-1} - t_{u-2}}{p_1\cdot p_2 \cdot\ldots\cdot p_{u-1}} = \frac{t_{v-1} - t_{u-2}}{s_{u-1}}\)\(B = \frac{s_v}{s_{u-1}}\),二者相除即是期望天數.

神奇的代碼
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

int n,q;

const int N=2e5+8;

const long long mo=998244353;

long long psum[N],pmul[N],ipmul[N],p[N];

long long ans;

set<long long> mirror;

long long kuai(long long a,long long b){
    long long qwq=1;
    while(b){
        if (b&1) qwq=qwq*a%mo;
        a=a*a%mo;
        b>>=1;
    }
    return qwq;
}
void Input(void) {
    read(n);
    read(q);
    long long tmp=kuai(100,mo-2);
    for(int i=1;i<=n;++i){
        read(p[i]);
        p[i]=p[i]*tmp%mo;
    }
}

long long getans(long long l,long long r){
    long long qwq=0;
    if (l==1) qwq=0;
    else qwq=psum[l-2];
    return (pmul[l-1]*(psum[r-1]-qwq)%mo*ipmul[r]%mo*ipmul[l-1])%mo;
}

void Solve(void) {
    pmul[0]=1;
    ipmul[0]=1;
    psum[0]=1;
    for(int i=1;i<=n;++i){
        pmul[i]=pmul[i-1]*p[i]%mo;
        psum[i]=(psum[i-1]+pmul[i])%mo;
        ipmul[i]=kuai(pmul[i],mo-2);
    }
    ans=getans(1,n);
    mirror.insert(1);
    mirror.insert(n+1);
    for(int u,i=1;i<=q;++i){
        read(u);
        if (mirror.count(u)){
            auto mid=mirror.find(u);
            auto l=mid,r=mid;
            --l;
            ++r;
            ans-=getans(*l,*mid-1);
            ans-=getans(*mid,*r-1);
            ans+=getans(*l,*r-1);
            mirror.erase(u);
        }
        else{
            auto mid=mirror.insert(u).first;
            auto l=mid,r=mid;
            --l;
            ++r;
            ans+=getans(*l,*mid-1);
            ans+=getans(*mid,*r-1);
            ans-=getans(*l,*r-1);
        }
        ans%=mo;
        while(ans<0) ans+=mo;
        printf("%lld\n",ans);
    }
}

void Output(void) {}

main(void) {
    //int kase;
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    //read(kase);
    //for (int i = 1; i <= kase; i++) {
        //printf("Case #%d: ", i);
        Input();
        Solve();
        Output();
    //}
}


F. Beautiful Bracket Sequence (easy version)

題目大意

定義一個合法的括號串以及它的深度:

  • 合法的括號串即左括號數量等於右括號數量且對於任意一個左括號\((\)都能找到一個與之匹配的右括號\()\)
  • 空括號串的深度為\(0\)
  • 當一個合法括號串\(t\)的深度為\(d\),則\(\left( t\right)\)括號串的深度為\(d+1\)
  • 如果有兩個合法的括號串\(s\)\(t\),則括號串\(st\)的深度為兩者中的較大值

則對於一個(可能不合法)的括號串,它的深度為它所有合法括號子串(可以不連續)深度的最大值。
現給定一個含有"(" "?" 和")"括號串,對"?"取所有值("("或")"),求所有情況該括號串深度和。

解題思路

我們先解決如何計算一個括號串的深度。
這是個區間問題,我們定義\(dp\left[ i \right] \left[ j \right]\)表示\(s\left[ i...j\right]\)的深度
如果\(s[i] = (\)\(s[j] = )\)或者其中有一個或兩個是\(?\),那么\(dp\left[ i \right] \left[ j \right] =dp\left[ i+1 \right] \left[ j-1 \right] +1\)
否則\(dp\left[ i \right] \left[ j \right] =max\left( dp\left[ i+1 \right] \left[ j \right],dp\left[ i \right] \left[ j-1 \right]\right)\)
這就解決了深度的問題。
從中我們考慮每對括號對答案的貢獻,即設\(dp\left[ i \right] \left[ j \right]\)表示\(s\left[ i...j\right]\)中的括號取遍所有情況,該串的深度和。
\(s[i] \neq '('\)時,此時\(s[i]\)\(s[j]\)對答案沒有任何貢獻,那么\(dp[i][j] += dp[i+1][j]\)
\(s[j] \neq ')'\)時,此時\(s[i]\)\(s[j]\)對答案沒有任何貢獻,那么\(dp[i][j] += dp[i][j-1]\)
注意到如果\(s[i] \neq '('\)\(s[j] \neq ')'\),我們加上了兩次\(dp[i+1][j-1]\)的貢獻,此時要\(dp[i][j] -= dp[i+1][j-1]\)
而如果\(s[i] = (\)\(s[j] = )\)或者其中有一個或兩個是\(?\),即\(s[i] \neq ')'\)\(s[j] \neq '('\),這個時候\(s[i]\)\(s[j]\)可以形成一對括號,對答案貢獻了1,若區間\([i+1,j-1]\)\(k\)個問號,則有\(2^k\)種情況,此時對答案貢獻為\(2^k\),故\(dp[i][j] += dp[i+1][j-1] + 2^k\)
綜上,即:

  • \(s[i] \neq '('\) , \(dp[i][j] += dp[i+1][j]\)
  • \(s[j] \neq ')'\) , \(dp[i][j] += dp[i][j-1]\)
  • \(s[i] \neq '('\) && \(s[j] \neq ')'\) , \(dp[i][j] -= dp[i+1][j-1]\)
  • \(s[i] \neq ')'\) && \(s[j] \neq '('\) , \(dp[i][j] += dp[i+1][j-1] + 2^k\)

區間DP。

神奇的代碼
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

const long long mo=998244353;

const int N=2e3+8;

char s[N];

long long sum[N],dp[N][N];

int len;

void Input(void) {
    scanf("%s",s);
}

long long kuai(long long a,long long b){
    long long qwq=1;
    while(b){
        if (b&1) qwq=qwq*a%mo;
        a=a*a%mo;
        b>>=1;
    }
    return qwq;
}

void Solve(void) {
    len=strlen(s);
    sum[0]=(s[0]=='?');
    for(int i=1;i<len;++i) sum[i]=sum[i-1]+(s[i]=='?');
    for(int l=2;l<=len;++l)
        for(int j,i=0;i<len-l+1;++i){
            j=i+l-1;
            if (s[i]!='(') dp[i][j]=(dp[i][j]+dp[i+1][j])%mo;
            if (s[j]!=')') dp[i][j]=(dp[i][j]+dp[i][j-1])%mo;
            if (s[i]!='('&&s[j]!=')') dp[i][j]=(dp[i][j]-dp[i+1][j-1]+mo)%mo;
            if (s[i]!=')'&&s[j]!='(') dp[i][j]=((dp[i][j]+dp[i+1][j-1])%mo+kuai(2ll,sum[j-1]-sum[i]))%mo;
        }
}

void Output(void) {
    printf("%lld\n",dp[0][len-1]);
}

main(void) {
    //int kase;
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    //read(kase);
    //for (int i = 1; i <= kase; i++) {
        //printf("Case #%d: ", i);
        Input();
        Solve();
        Output();
    //}
}


這次的題都是貪心構造和期望和DPqwq。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM