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\n",res*((k+1)>>1)+sum*(k>>1)),0;
        return printf("%lld\n",res+sum*(k-1)),0;
    }
    res*=k;
    printf("%lld\n",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\n",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\n",mx);
    return 0;
}

\(C\)

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

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

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

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

由于还需要字典序小于\(X\),那么我们考虑\(X\)的前\({k\over 2}\)位,只要\(T\)的字典序小于它那么后面必定小于,顺便特判一下\(T\)和\(X\)的前\({k\over 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\n",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\n",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\n",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_i\leq k\)的\(i\)行,\(Y_i\leq k\)的\(j\)列,此时的方案总数,转移分两步

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

第二步,枚举\(Y_i=k+1\)的列数,那么对于每一个这样的列,在矩阵\(B\)已经考虑完\(X_i\leq k+1\)的那\(i\)行中,显然是可以任取\(\geq k+1\)的数,且至少得有一个\(k+1\),在矩阵\(A\)还没有考虑\(X_i\leq 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\n",f[L+1][n][m][0]);
    return 0;
}

原文地址:https://www.cnblogs.com/yuanquming/p/11626378.html

时间: 2024-10-08 08:05:11

AtCoder Grand Contest 039 题解的相关文章

AtCoder Grand Contest 007题解

传送门 \(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<

AtCoder Grand Contest 008题解

传送门 \(A\) 分类讨论就行了 然而我竟然有一种讨论不动的感觉 int x,y; inline int min(R int x,R int y){return x<y?x:y;} inline int min(R int a,R int b,R int c,R int d){ return min(min(a,b),min(c,d)); } inline int calc(R int x,R int y){return y>=x?y-x:x-y+2;} int main(){ scanf(

AtCoder Grand Contest 014题解

传送门 \(A\) 首先大力猜测一下答案不会很大,所以次数大于\(10^6\)输出\(-1\)就行了 不过我并不会证上界,据说是因为如果\(a=b=c\)且都是偶数肯定\(-1\),否则设\(a\leq b\leq c\),则最大最小值的差为\(c-a\),一次操作之后变成了\({c-a\over 2}\),所以操作次数就是\(\log\)级别的了 typedef long long ll; ll a,b,c,sum;int res; int main(){ scanf("%lld%lld%ll

Atcoder Grand Contest 003 题解

A - Wanna go back home 如果有S就必须要有N,反之亦然,如果有E必须要有W,反之亦然.判断一下就好了. //waz #include <bits/stdc++.h> using namespace std; #define mp make_pair #define pb push_back #define fi first #define se second #define ALL(x) (x).begin(), (x).end() #define SZ(x) ((int

AtCoder Grand Contest 008 题解

A - Simple Calculator 模拟+分类讨论即可. //waz #include <bits/stdc++.h> using namespace std; #define mp make_pair #define pb push_back #define fi first #define se second #define ALL(x) (x).begin(), (x).end() #define SZ(x) ((int)((x).size())) typedef pair<

AtCoder Grand Contest 013 题解

A - Sorted Arrays 贪心,看看不下降和不上升最长能到哪,直接转移过去即可. 1 //waz 2 #include <bits/stdc++.h> 3 4 using namespace std; 5 6 #define mp make_pair 7 #define pb push_back 8 #define fi first 9 #define se second 10 #define ALL(x) (x).begin(), (x).end() 11 #define SZ(x

AtCoder Grand Contest 015 题解

A - A+...+B Problem 可以取到的值一定是一段区间.所以答案即为max-min+1 1 //waz 2 #include <bits/stdc++.h> 3 4 using namespace std; 5 6 #define mp make_pair 7 #define pb push_back 8 #define fi first 9 #define se second 10 #define ALL(x) (x).begin(), (x).end() 11 #define

AtCoder Grand Contest 006 题解

传送门 \(A\) 咕咕 const int N=105; char s[N],t[N];int n; inline bool eq(R int k){fp(i,1,k)if(s[n-k+i]!=t[i])return false;return true;} int main(){ scanf("%d%s%s",&n,s+1,t+1); fd(k,n,0)if(eq(k))return printf("%d\n",(n<<1)-k),0; ret

AtCoder Grand Contest 013题解

传送门 \(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