The 2021 ICPC Asia Kunming Regional Contest

The 2021 ICPC Asia Kunming Regional Contest

D-Divisions

做法:我们发现,当构造成a a a b b b c c c \((a<b<c)\)这样的时候,答案就是\(2^{num(a)}-1+2^{num(b)}-1+2^{num(c)}-1+1\),我们选ac,只剩b时就可以,或者ab,只剩c,类似这样的选法,有\(2^{x}-1\)种,而最后可以全选再加一种,所以我们构造出来的序列方案数,就是上述式子求和,只需要从高位往下一直构造,肯定能构造任意的k,

点击查看代码
#include <bits/stdc++.h>
using namespace std;

bool check(int k){
    int x=1;
    while(x<=k){
        if(x==k) return 1;
        x=x*2;
    }
    return 0;
}

int main(){
    #ifdef lmj_debug
        freopen("1.in","r",stdin);
    #endif
    int a[30];
    for (int i=0;i<=30;i++) a[i]=(1<<i);
    int k;
    cin>>k;
    vector<int>ans;
    if(k==0){
        printf("8\n");
        printf("1 100 50 78 87 1 2 100");
        return 0;
    }
    if(k==1){
        printf("6\n");
        printf("1 1 4 5 1 4");
        return 0;
    }
    if(check(k)){
        int p=log2(k);
        for (int i=1;i<=p;i++) ans.push_back(1);
    }else {
        int top=1;
        while(k>1){
            for (int i=30;i>=0;i--){
                if(k==1) break;
                if(k>=a[i] && k-(a[i]-1)>=1){
                    k-=(a[i]-1);
                    for (int j=1;j<=i;j++) ans.push_back(top);
                    top++;
                }
            }
        }
    }
    printf("%d\n",ans.size());
    for (auto x:ans){
        printf("%d ",x);
    }
    return 0;
}

E—Easy String Problem

做法:我们用[L,R]表示删除[L,R]区间后形成的序列,那么我们计算不同的序列个数就是\(L*(n-R+1)\)
但是会有重复的,对于同样长度的区间,如果[x,y]=[x-1,y-1],那么就意味着该区间只能算[x,y]一次,所以减去重复的就等价于\(L*(n-R+1)-[x,y]==[x-1,y-1]的区间的数量\) \((x<=L-1,y>=R-1)\),而出现这样的区间就等价于存在a[l-1]==a[r],所以我们需要统计的就是有多少对(p,q)满足\(a[p]==a[q]\)\((p<=L-1,q>=R+1)\),该题可以离线,所以我们可以莫队维护,在做莫队的时候一定要想清楚当前区间[l,r]的答案表示的是什么,赛上就是出现这个问题,Wa了无数发,

点击查看代码
#include <bits/stdc++.h>
using namespace std;

const int maxn=2e5+10;
typedef long long ll;
int N;

struct my{
    int l,r,id;

    bool operator<(const my &rhs)const {
        if(l/N!=rhs.l/N) return l<rhs.l;
        return ( (l/N)&1 ) ? r < rhs.r : r > rhs.r;
    }
};

my Q[maxn];
int a[maxn];
ll Ans[maxn];
ll ans=0,tot=0;
int pre[maxn],Next[maxn];

void delp(int x){
    tot-=Next[x];
    pre[x]--;
}

void deln(int x){
    tot-=pre[x];
    Next[x]--;
}

void addp(int x){
    tot+=Next[x];
    pre[x]++;
}

void addn(int x){
    tot+=pre[x];
    Next[x]++;
}

int main(){
    #ifdef lmj_debug
        freopen("1.in","r",stdin);
    #endif
    int n;
    scanf("%d",&n);
    N=sqrt(n);
    for (int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    int q;
    scanf("%d",&q);
    for (int i=1;i<=q;i++){
        scanf("%d%d",&Q[i].l,&Q[i].r);
        Q[i].id=i;
    }
    sort(Q+1,Q+1+q);
    int l=1,r=n;
    for (int i=1;i<=q;i++){
        int L=Q[i].l;
        int R=Q[i].r;
        while (l > L) delp(a[--l]);
        while (r < R) deln(a[++r]);
        while (l < L) addp(a[l++]);
        while (r > R) addn(a[r--]);
        int tot2=0;
        Ans[Q[i].id]=(ll)(Q[i].l)*(ll)((ll)n-Q[i].r+1ll)-tot;
    }
    for (int i=1;i<=q;i++){
        printf("%lld\n",Ans[i]);
    }
    return 0;
}

A Amino Acids

模拟题,搜索的部分很简单,就在于输出有点麻烦,但是可以把式子分成几部分输出,因为很多地方其实都是一样的,总的来说这道模拟没有非常困难,但是要注意细节

点击查看代码
#include <bits/stdc++.h>
using namespace std;

const int maxn=20;

map<string,int>mp;

int hi[maxn]={7,9,7,7,11,9,5,13,7,9};
int cnt[maxn]={89,132,133,121,146,147,75,149,105,119};

char G[maxn][maxn][maxn]={{
"  H H O    ",
"  | | ||   ",
"H-N-C-C-O-H",
"    |      ",
"  H-C-H    ",
"    |      ",
"    H      "
},{
"  H H O    ",
"  | | ||   ",
"H-N-C-C-O-H",
"    |      ",
"  H-C-H    ",
"    |      ",
"  O=C-N-H  ",
"      |    ",
"      H    "
},{
"  H H O    ",
"  | | ||   ",
"H-N-C-C-O-H",
"    |      ",
"  H-C-H    ",
"    |      ",
"  O=C-O-H  "
},{
"  H H O    ",
"  | | ||   ",
"H-N-C-C-O-H",
"    |      ",
"  H-C-S-H  ",
"    |      ",
"    H      "
},{
"  H H O    ",
"  | | ||   ",
"H-N-C-C-O-H",
"    |      ",
"  H-C-H    ",
"    |      ",
"  H-C-H    ",
"    |      ",
"  O=C-N-H  ",
"      |    ",
"      H    "
},{
"  H H O    ",
"  | | ||   ",
"H-N-C-C-O-H",
"    |      ",
"  H-C-H    ",
"    |      ",
"  H-C-H    ",
"    |      ",
"  O=C-O-H  "
},{
"  H H O    ",
"  | | ||   ",
"H-N-C-C-O-H",
"    |      ",
"    H      "
},{
"  H H O    ",
"  | | ||   ",
"H-N-C-C-O-H",
"    |      ",
"  H-C-H    ",
"    |      ",
"  H-C-H    ",
"    |      ",
"    S      ",
"    |      ",
"  H-C-H    ",
"    |      ",
"    H      "
},{
"  H H O    ",
"  | | ||   ",
"H-N-C-C-O-H",
"    |      ",
"  H-C-O-H  ",
"    |      ",
"    H      "
},{
"  H H O    ",
"  | | ||   ",
"H-N-C-C-O-H",
"    |      ",
"  H-C-O-H  ",
"    |      ",
"  H-C-H    ",
"    |      ",
"    H      "
}
};

int m,n;
vector<int>p;
vector< int >ans[100000];
int anstop;
vector<int>sta;

void dfs(int num,int sum){
    if(sum>n) return;
    if(num>1){
        for (int i=0;i<sta.size();i++) {
            ans[anstop].push_back(sta[i]);
        }
        anstop++;
    }

    for (int i=0;i<p.size();i++){
        sta.push_back(p[i]);
        dfs(num+1,sum+cnt[p[i]]-18);
        sta.pop_back();
    }
}

void print(){
    for (int t=0;t<anstop;t++){
        auto x=ans[t];
        printf("\n");
        int h=0;
        for (auto id:x){
            h=max(h,hi[id]);
        }
        //cout<<h<<endl;
        for (int i=0;i<2;i++){
            for (int j=0;j<x.size();j++){
                for (int k=0;k<8;k++)
                    printf("%c",G[x[j]][i][k]);
            }
            printf("\n");
        }//先打上面两行
        int oo=(int)x.size()-1;
        for (int j=0;j<=oo;j++){
            int k=0;
            if(j!=0) k=2;
            if(j!=oo){
                for (;k<8;k++)
                        printf("%c",G[x[j]][2][k]);
                printf("--");
            }else {
                for (;k<11;k++)
                        printf("%c",G[x[j]][2][k]);
            }
        }//打中间一行*/
        printf("\n");
        for (int i=3;i<h;i++){
            for (int j=0;j<x.size();j++){
                int k=0;
                if(j!=0) k=2;
                if(i>=hi[x[j]]) {
                    for (;k<10;k++) printf(" ");
                }else {
                    for (;k<10;k++)
                        printf("%c",G[x[j]][i][k]);
                }
            }
            printf("\n");
        }//先打上面两行
    }
}

int main(){
    #ifdef lmj_debug
        freopen("1.in","r",stdin);
    #endif
    mp["Ala"]=0;
    mp["Asn"]=1;
    mp["Asp"]=2;
    mp["Cys"]=3;
    mp["Gln"]=4;
    mp["Glu"]=5;
    mp["Gly"]=6;
    mp["Met"]=7;
    mp["Ser"]=8;
    mp["Thr"]=9;
    cin>>m>>n;
    for (int i=1;i<=m;i++){
        string s;
        cin>>s;
        p.push_back(mp[s]);
    }
    sort(p.begin(),p.end());
    for (int i=0;i<p.size();i++){
        sta.push_back(p[i]);
        dfs(1,cnt[p[i]]);
        sta.pop_back();
    }
    //sort(ans.begin(),ans.begin()+anstop);
    printf("%d",(int)anstop);
    print();
    return 0;
}

G-Glass Bead Game

题意:有n个小球排成一排,每个小球给出一个概率p[i],按照给出的概率随机选取一个小球,将其放到最前面,每次操作的代价是该球前面求的个数,问在操作无限次后再操作一次,操作代价的期望是多少.
做法:最终的期望就等于\(\sum_{i=1}^{n}p[i]*排在i前的个数\),所以我们还需要求排在i前的球个数的期望,这个等于\(\sum_{i!=j}\frac{p[j]}{p[j]+p[i]}\),

点击查看代码
#include <bits/stdc++.h>
using namespace std;

const int maxn=110;

double p[maxn];

int main(){
    #ifdef lmj_debug
        freopen("1.in","r",stdin);
    #endif
    int n;
    cin>>n;
    for (int i=1;i<=n;i++) scanf("%lf",&p[i]);
    double ans=0;
    for (int i=1;i<=n;i++){
        double res=0;
        for (int j=1;j<=n;j++){
            if(i!=j)
            res+=(p[j])/(p[i]+p[j]);//排在i之前的个数的期望
        }
        ans+=p[i]*(res);
    }
    printf("%.12lf\n",ans);
    return 0;
}

B Blocks

首先考虑怎么推期望,设状压dp[i]表示i状态距离铺满的状态所需要的期望步数,那么dp[0]就是答案,
对于状态i不是铺满的 \(dp[i]=1+\frac{1}{n}\sum_{j=0}^{n}{dp[i⊕(1<<j)]}\) 对于状态i = i⊕(1<<j)的,即需要用到dp[i]自身,设i中1的个数为cnt,image
这个时候简单推导一下,式子就成了

\[dp[i]=1+\frac{1}{n}*cnt*dp[i]+\frac{1}{n}*c \]

变换一下,就是$$dp[i]=\frac{n+c}{n-cnt}$$

所以只需要解决dp[i]=0的情况就可以了,就是面积铺满的情况,这个情况的话,我们可以用容斥定理,统计出每个块的交集,然后枚举子集用容斥定理通过交集求出总的并集,判断面积即可,感觉其实还是蛮难做的一道题,不知道榜上为啥那么多人过,感觉G和E其实更好想,也更好写

点击查看代码
#include <bits/stdc++.h>
using namespace std;

const int N=12;
typedef long long ll;
const ll mod=998244353;

struct my{
    int x,y;
}a[N],b[N];

ll dp[1<<N],area[1<<N],f[1<<N];

inline int check(int x,int n){
    int num=0;
    for (int i=0;i<n;i++) if(((x>>i)&1)) num++;
    return (num&1) ? 1 : -1;
}

ll p[N];

ll quickpow(ll a,ll b){
    ll ans=1;
    for (;b;b>>=1){
        if(b&1ll) ans=ans*a%mod;
        a=a*a%mod;
    }
    return ans%mod;
}

int main(){
    #ifdef lmj_debug
        freopen("1.in","r",stdin);
    #endif
    int _;
    cin>>_;
    for (int i=0;i<N;i++) p[i]=quickpow(i,mod-2);
    while(_--){
        int n;
        int W,H;
        scanf("%d",&n);
        cin>>W>>H;
        for (int i=0;i<n;i++) scanf("%d%d%d%d",&a[i].x,&a[i].y,&b[i].x,&b[i].y);
        for (int i=1;i<(1<<n);i++){
            area[i]=0;
            int xl=0,yl=0,xr=W,yr=H;
            for (int j=0;j<n;j++){
                if(((i>>j)&1)){
                    xl=max(a[j].x,xl);
                    xr=min(b[j].x,xr);
                    yl=max(a[j].y,yl);
                    yr=min(b[j].y,yr);
                }
            }
            if(xr<xl) xr=xl;
            if(yr<yl) yr=yl;
            area[i]=(ll)(xr-xl)*(yr-yl);
        }
        for (int i=1;i<(1<<n);i++){
            dp[i]=0;
            for (int s=i;s;s=(s-1)&i){//枚举i的子集
                dp[i]=dp[i]+area[s] * check(s,n);
            }
            //printf("%lld\n",dp[i]);
        }
       // cout<<dp[(1<<n)-1]<<" "<<W*H<<endl;
        if(dp[(1<<n)-1]!=(ll)W*H){
            printf("-1\n");
            continue;
        }
        ll mj=(ll)W*H;
        for (int i=(1<<n)-1;i>=0;i--){
            f[i]=0;
            ll cnt=0,num=0;
            if(dp[i]==mj) continue;
            for (int j=0;j<n;j++){
                if(((i>>j)&1)) cnt++;
                else num=(num+f[(i^(1<<j))])%mod;
            }
            f[i]=(n+num)*(p[n-cnt])%mod;
        }
        printf("%lld\n",f[0]%mod);
    }
    return 0;
}
posted @ 2022-04-27 20:44  lmj_1  阅读(44)  评论(0编辑  收藏  举报