AtCoder Grand Contest 039 题解

Posted yuanquming

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AtCoder Grand Contest 039 题解相关的知识,希望对你有一定的参考价值。

传送门

(A)

首先只有一串的情况下,遇到相同的肯定是改后面那一个最优,然后两串的话可能要分奇偶讨论一下

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=105;
typedef long long ll;
char s[N];int t[N],n,k;ll res,sum;
int main(){
    scanf("%s%d",s+1,&k),n=strlen(s+1);
    fp(i,1,n)t[i]=s[i];
    fp(i,2,n)if(t[i]==t[i-1])++res,t[i]=2333;
    if(t[n]==t[1]){
        fp(i,1,n)t[i]=s[i];t[1]=2333,++sum;
        fp(i,2,n)if(t[i]==t[i-1])++sum,t[i]=2333;
        if(t[n]==2333)return printf("%lld
",res*((k+1)>>1)+sum*(k>>1)),0;
        return printf("%lld
",res+sum*(k-1)),0;
    }
    res*=k;
    printf("%lld
",res);
    return 0;
}

(B)

首先有奇环肯定无解,否则我们枚举哪个点是(1)号点,用(bfs)依次确定剩下的每个点的编号,因为图中不存在奇环所以这样跑出来的肯定合法

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=205,M=5e5+5;
struct eg{int v,nx;}e[M];int head[N],tot;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
char s[N];int vis[N],ins[N],col[N],q[N],cnt,tim,n,mx,fl;
void dfs(int u,int d){
    col[u]=d,ins[u]=1;
//  printf("qwq %d %d %d
",u,d,col[u]);
    go(u)if(!ins[v])dfs(v,d^1);
        else if(col[v]!=(d^1))fl=1;
}
void bfs(R int s){
    R int h=1,t=0,u;
    ++tim,q[++t]=s,vis[s]=tim,col[s]=1;
    while(h<=t){
        u=q[h++];
        go(u)if(vis[v]!=tim)vis[v]=tim,col[v]=col[u]+1,q[++t]=v;
            else assert(abs(col[u]-col[v])==1);
    }
    cmax(mx,col[q[t]]);
}
int main(){
    scanf("%d",&n);
    fp(i,1,n){
        scanf("%s",s+1);
        fp(j,1,n)if(s[j]=='1')add(i,j);
    }
    dfs(1,0);if(fl)return puts("-1"),0;
    fp(i,1,n)bfs(i);
    printf("%d
",mx);
    return 0;
}

(C)

把操作放到二进制意义下考虑,就是每次把最低位取反然后放到最高位,反过来就可以看成是把最高位取反放到最低位,不难发现任何一个数最多(2n)次之后必定会变回原数

那么每一个数最少需要的次数(k)肯定是(2n)的因子,鉴于直接计算很麻烦,我们计算(k)次之后相等的数的个数,然后容斥即可得到最少(k)次之后相等的个数

对于一个(n)位的二进制数(S),我们把(S)按位取反之后接在后面得到一个长为(2n)的数,那么(S)(k)次操作之后相等当且仅当这个长为(2n)的数以(k)为一个周期且({2nover k})必须是奇数
(如果({2nover k})是偶数说明(S)和它按位取反之后的那个数相等了,显然不可能)

进一步考虑之后我们发现,整个(S)必定是由一个长为({kover 2})的串(T)和它的按位取反轮流拼接而成的,所以我们只要数出合法的(T)的个数即可

由于还需要字典序小于(X),那么我们考虑(X)的前({kover 2})位,只要(T)的字典序小于它那么后面必定小于,顺便特判一下(T)(X)的前({kover 2})位相等的情况就行了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int P=998244353;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
    R int res=1;
    for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
    return res;
}
const int N=5e5+5;
char s[N];int a[N],b[N],c[N],st[N],n,res,tot,t;
int main(){
//  freopen("testdata.in","r",stdin);
    scanf("%d%s",&n,s+1);
    fp(i,1,n)a[i]=s[i]-'0';
    n<<=1;
    fp(k,1,n)if(n%k==0&&((n/k)&1)){
        ++tot,b[tot]=k,c[tot]=0;
        fp(i,1,k>>1)c[tot]=((c[tot]<<1ll)+a[i])%P;
        fp(i,1,k>>1)st[i]=a[i];
        fp(i,(k>>1)+1,n>>1)st[i]=st[i-(k>>1)]^1;
        ++c[tot];
        fp(i,(k>>1)+1,n>>1)if(a[i]!=st[i]){
            c[tot]-=(a[i]<st[i]);
            break;
        }
    }
    fp(i,1,tot)fp(j,1,i-1)if(b[i]%b[j]==0)upd(c[i],P-c[j]);
    fp(i,1,tot)upd(res,mul(c[i],b[i]));
    printf("%d
",res);
    return 0;
}

(D)

数学太差,没有办法……

前置芝士(1)

假设圆上的三点分别为(A,B,C),三角形(ABC)的内心为(O),令(A')(AO)与单位圆的另一个交点(不难发现(A')也是(BC)这一段弧的中点),同理定义(B',C'),则三角形(A'B'C')的垂心与(O)重合

证明:自行画图理解

前置芝士(2)

对于任意一个三角形,它的重心(G),垂心(H),外心(O)三点共线,且(2|GO|=|GH|)

证明:自行百度"欧拉线"

题解

因为所有的点都在单位圆上,那么(ABC)的内心即为(A'B'C')的垂心

而因为(A'B'C')的外心就是原点,重心就是三个点的坐标的平均值,那么我们只要算出重心的期望,就可以推出垂心的期望了。而(A')的坐标只和(BC)有关,与(A)无关(只要保证(A)不在(A')这段弧上即可),那么我们枚举(BC),计算对应的(A')的贡献就行了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=3005;double Pi=acos(-1.0);
double x[N],y[N],rx,ry,tmp;int a[N],n,L;
int main(){
//  freopen("testdata.in","r",stdin);
    scanf("%d%d",&n,&L);Pi*=2.0/L;
    fp(i,1,n)scanf("%d",&a[i]);
    fp(i,1,n)fp(j,1,i-1){
        R int t=i-j-1;
        if(t){
            rx+=t*-cos((a[i]+a[j])*0.5*Pi);
            ry+=t*-sin((a[i]+a[j])*0.5*Pi);
        }
        t=n-i+j-1;
        if(t){
            rx+=t*cos((a[i]+a[j])*0.5*Pi);
            ry+=t*sin((a[i]+a[j])*0.5*Pi);
        }
    }
    tmp=1.0*n*(n-1)*(n-2)/2;
    rx/=tmp,ry/=tmp;
    rx*=3,ry*=3;
    printf("%.10lf %.10lf
",rx,ry);
    return 0;
}

(E)

好迷的题目啊……

首先把环从(2n)(1)那里断开变成一条链,然后假设与(1)配对的点是(i)

那么我们接下来就要对([2,i)cup (i,n])之间的点继续配对,因为边要构成一棵树,所以跨过两个区间的边至少要有一条,且如果有多条时端点要单调

枚举最靠外侧的横跨两个区间的边((j,k)),然后先考虑((j,i))之间的点,它们连出的边要么和(j)连出的这条边相连,要么和(i)连出的这条边相连,且一定存在一个分界点(p),由于((j,k))是最靠外侧的横跨区间的边,所以对于([2,p])之间的点我们不需要再知道([i,n])的信息,那么可以递归为一个([2,j)cup (j,p])的子问题

同理定义((i,k))的分界点(q),那么后面这个可以递归为一个([q,k)cup (k,n])的子问题。而中间那部分就是一个([p+1,i)cup (i,q-1])的子问题

那么(dp)就行了,复杂度(O(n^7))

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef long long ll;
const int N=55;
char s[N][N];ll f[N][N][N];int n;
inline ll calc(R int l,R int r,R int m){
    if(~f[l][r][m])return f[l][r][m];
    if(l==r)return f[l][r][m]=1;
    if(l==m||r==m)return f[l][r][m]=0;
    R ll res=0;
    fp(j,l,m-1)fp(k,m+1,r)if(s[j][k]=='1')
        fp(p,j,m-1)fp(q,m+1,k)
            res+=calc(l,p,j)*calc(p+1,q-1,m)*calc(q,r,k);
    return f[l][r][m]=res;
}
int main(){
    memset(f,-1,sizeof(f));
    scanf("%d",&n);
    fp(i,1,n<<1)scanf("%s",s[i]+1);
    R ll res=0;
    fp(i,2,n<<1)if(s[1][i]=='1')res+=calc(2,n<<1,i);
    printf("%lld
",res);
    return 0;
}

(F)

好玄学的(dp)……

首先,对于一个矩阵(B),它的权值等价于重新填一个矩阵(A),且(A[i][j])要小于等于(B[i][j])对应位置上的那(n+m-1)个值中的最小值的方案数,等价于(A)中每行的最大值小于等于(B)中对应行的最小值,(A)中每列的最大值小于等于(B)中对应列的最小值,的方案数

那么最终答案可以转化为所有合法的矩阵(A,B)的个数

我们记(X_i)(A)中第(i)行的最大值,(Y_i)(B)中第(i)列的最小值,记(dp[i][j][k])表示已经考虑完了所有(X_ileq k)(i)行,(Y_ileq k)(j)列,此时的方案总数,转移分两步

第一步,枚举(X_i=k+1)的行数,那么对于每一个这样的行,在矩阵(B)已经考虑完(Y_ileq k)的那(j)列中,显然是可以任取(geq k+1)的数,而在矩阵(A)还没有考虑的那(m-j)列中,显然是可以任取(leq k+1)的数,且得保证至少有一个(k+1)

第二步,枚举(Y_i=k+1)的列数,那么对于每一个这样的列,在矩阵(B)已经考虑完(X_ileq k+1)的那(i)行中,显然是可以任取(geq k+1)的数,且至少得有一个(k+1),在矩阵(A)还没有考虑(X_ileq k+1)的那(n-i)行中,可以任取(leq k+1)的数

那么额外记一个(0/1)表示转移的两步,预处理转移系数,时间复杂度为(O(nmk(n+m)))

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
int P;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
    R int res=1;
    for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
    return res;
}
const int N=105;
int f[N][N][N][2],coef[N][N][N][2],bn[N][N];
int n,m,L,ret,cp;
int main(){
    scanf("%d%d%d%d",&n,&m,&L,&P);
    fp(i,0,max(n,m)){
        bn[i][0]=1;
        fp(j,1,i)bn[i][j]=add(bn[i-1][j],bn[i-1][j-1]);
    }
    fp(k,1,L)fp(i,0,m){
        cp=mul(ksm(L-k+1,i),dec(ksm(k,m-i),ksm(k-1,m-i)));
        ret=1;
        fp(j,0,n)coef[k][i][j][0]=ret,ret=mul(ret,cp);
    }
    fp(k,1,L)fp(i,0,n){
        cp=mul(dec(ksm(L-k+1,i),ksm(L-k,i)),ksm(k,n-i));
        ret=1;
        fp(j,0,m)coef[k][i][j][1]=ret,ret=mul(ret,cp);
    }
    f[1][0][0][0]=1;
    fp(k,1,L)fp(i,0,n)fp(j,0,m){
        ret=f[k][i][j][0];
        fp(l,0,n-i)upd(f[k][i+l][j][1],1ll*ret*bn[n-i][l]%P*coef[k][j][l][0]%P);
        ret=f[k][i][j][1];
        fp(l,0,m-j)upd(f[k+1][i][j+l][0],1ll*ret*bn[m-j][l]%P*coef[k][i][l][1]%P);
    }
    printf("%d
",f[L+1][n][m][0]);
    return 0;
}

以上是关于AtCoder Grand Contest 039 题解的主要内容,如果未能解决你的问题,请参考以下文章

AtCoder Grand Contest 039 题解

AtCoder Grand Contest 039

Atcoder Grand Contest 039B(思维,BFS)

Atcoder Grand Contest 039C(容斥原理,计数DP)

AtCoder Grand Contest 039 F: Min Product Sum

AtCoder Grand Contest 039 F: Min Product Sum