CF1864 Harbour.Space Scholarship Contest 2023-2024 (Div. 1 + Div. 2)

好久没打CF/ATC正式赛了,果然一打就暴毙,一晚上连续被ABC和div1+2干碎

C

A-F除了C都没什么好说的,C没像题解那样想到二进制,之后看了讨论感觉比较顺的思路是:偶数直接/2肯定没问题,后面减的数肯定更小;奇数的情况,即使减掉一个最大的约数,也难以保证前者的性质,考虑先-1变成2k,然后把k->1的过程分别乘2,就可以映射到2k->2的过程上,并且每次减的数都>1,最后再-1即可!

G

题目最后给的三个限制其实提示的比较明显了:前两个限制说明,每个数都是经过两次所在行/列移动到达目标位置(即(x1,y1)->(x1,y2)/(x2,y1)->(x2,y2));那进一步考虑一行或一列能移动的条件:即每个数到目标位置的列/行偏移量都相同;所以找出这样的行和列,发现行之间和列之间的顺序都是无关的,直接乘阶乘;而对行列交叉的位置,发现要到达目的,必须得有两行或两列的位移量相同,与第三个限制矛盾!于是先判断第三个限制,在合法的情况下,不断地按上述过程移动和计算,则一定满足要求。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=505,P=998244353;
int n,a[N][N],b[N][N],fc[N];
bool chk(){
    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(a[i][j]!=b[i][j]) return 0;
    return 1;
}
struct node{
    int x,y;
}pos[N*N];
int d[N],t[N];
int chx(int x){
    for(int i=1;i<=n;i++) d[i]=(pos[a[x][i]].y-i+n)%n;
    for(int i=1;i<n;i++) if(d[i]!=d[i+1]) return 0;
    return d[1];
}
int chy(int y){
    for(int i=1;i<=n;i++) d[i]=(pos[a[i][y]].x-i+n)%n;
    for(int i=1;i<n;i++) if(d[i]!=d[i+1]) return 0;
    return d[1];
}
void shx(int x,int z){
    for(int i=1;i<=n;i++) t[(i+z-1)%n+1]=a[x][i];
    for(int i=1;i<=n;i++) a[x][i]=t[i];
}
void shy(int y,int z){
    for(int i=1;i<=n;i++) t[(i+z-1)%n+1]=a[i][y];
    for(int i=1;i<=n;i++) a[i][y]=t[i];
}
void work(){
    cin>>n;
    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&a[i][j]);
    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){
        scanf("%d",&b[i][j]);
        pos[b[i][j]]={i,j};
    }
    map< pair<int,int>,bool >mp; mp.clear();
    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){
        node v=pos[a[i][j]];
        pair<int,int> du={(v.x-i+n)%n,(v.y-j+n)%n};
        if(!du.first || !du.second) continue;
        if(mp[du]){
            puts("0");
            return;
        }
        mp[du]=1;
    }
    int s=1;
    while(!chk()){
        int cx=0,cy=0,ax[N],ay[N];
        for(int i=1;i<=n;i++){
            ax[i]=chx(i);
            if(ax[i]) cx++;
        }
        for(int i=1;i<=n;i++){
            ay[i]=chy(i);
            if(ay[i]) cy++;
        }
        //cout<<"cx="<<cx<<" cy="<<cy<<endl;
        if(cx && cy || (!cx && !cy)){
            puts("0");
            return;
        }
        s=1ll*s*fc[cx]%P*fc[cy]%P;
        for(int i=1;i<=n;i++) if(ax[i]) shx(i,ax[i]);
        for(int i=1;i<=n;i++) if(ay[i]) shy(i,ay[i]);
    }
    cout<<s<<endl;
}
int main() {
    fc[0]=1; for(int i=1;i<N;i++) fc[i]=1ll*fc[i-1]*i%P;
    int T; cin>>T; while(T--) work();
    return 0;
}

H

从统计\(+1\)\(\times 2\)这两个元素的合法序列考虑,发现第一次不小于x就停下这个限制,还得根据最后一个是\(+1\)还是\(\times 2\)分讨,比较麻烦;考虑算贡献的方式:可以看出,每个<x的状态对答案都有1的贡献,这样每个合法的序列恰好会被算到它的步数次的贡献。
于是f[i]表示i以内的概率和,转移直接\(f[i]=\frac{f[i/2]+f[i-1]}{2}+1\),然后维护\((f[i],f[i/2],...,f[i/2^m])\),考虑从\((f[i-1],f[(i-1)/2],...,f[(i-1)/2^m])\)转移,发现转移矩阵只与i的后缀0个数有关,于是推一下后缀0个数的变化规律,发现可以预处理一个前2^k的转移矩阵,询问时依次乘起来即可。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=61,P=998244353,V=(P+1)/2;
void inc(int& x,int y){
    x+=y;
    if(x>=P) x-=P;
}
int sum(int x,int y){
    inc(x,y);
    return x;
}
int prd(int x,int y){
    return 1ll*x*y%P;
}
struct mtx{
    int a[N][N];
    void init(int x){
        for(int i=0;i<N;i++) for(int j=0;j<N;j++){
                a[i][j]=x*(i==j);
            }
    }
    void print(int x){
        cout<<"print:"<<x<<endl;
        for(int i=0;i<N;i++){
            for(int j=0;j<N;j++) cout<<a[i][j]<<" ";
            puts("");
        }
    }
    mtx operator * (mtx& u) {
        mtx v;
        v.init(0);
        for(int k=0;k<N;k++){
            for(int i=0;i<N;i++) for(int j=0;j<N;j++) inc(v.a[i][j],prd(a[i][k],u.a[k][j]));
        }
        return v;
    }
    mtx operator + (mtx& u) {
        mtx v;
        for(int i=0;i<N;i++) for(int j=0;j<N;j++) v.a[i][j]=sum(a[i][j],u.a[i][j]);
        return v;
    }
};
struct vec{
    int a[N];
    vec(int _n){
        for(int j=0;j<N;j++){
            if(!j) a[j]=_n;
            else a[j]=0;
        }
    }
    void put(){
        for(int i=0;i<N;i++) cout<<a[i]<<" "; puts("");
    }
    vec operator * (mtx u){
        vec v(0);
        for(int k=0;k<N;k++){
            for(int i=0;i<N;i++) inc(v.a[i],prd(a[k],u.a[i][k]));
        }
        return v;
    }
};
ll pw[N];
mtx A[N],S[N];
void pre(){
    for(int i=0;i<N-1;i++){
        pw[i]=(1ll<<i);
        A[i].init(0);
        for(int j=N-2;j>=0;j--){
            if(j>i) A[i].a[j][j]=1;
            else{
                for(int k=0;k<N;k++) A[i].a[j][k]=prd(A[i].a[j+1][k],V);
                inc(A[i].a[j][j],V);
                inc(A[i].a[j][N-1],1);
            }
        }
        A[i].a[N-1][N-1]=1;
        if(!i) S[i].init(1);
        if(i<N-2) S[i+1]=S[i]*A[i]*S[i];
    }
    for(int i=0;i<N-1;i++) S[i]=A[i]*S[i];//S[i].print(i);
}
int main(){
    pre();
    //puts("!!");
    int T;
    cin>>T;
    while(T--){
        ll n;
        cin>>n;
        n--;
        vec u(0); u.a[N-1]=1;
        //cout<<"n="<<n<<endl;
       // cout<<u.a[0][N-1]<<endl;
        for(int i=N-2;i>=0;i--) if(n&pw[i]){
           // cout<<"i="<<i<<endl;
            u=u*S[i];
            //u.put();
        }
        int ans=u.a[0];
        //cout<<prd(ans,1<<15)<<endl;
        //for(int i=0;i<N;i++) cout<<u.a[i][N-1]<<" "; puts("");
        //cout<<u.a[0][N-1]<<endl;
        cout<<ans<<endl;
    }
}
posted @ 2023-08-28 22:58  sz[sz]  阅读(18)  评论(0编辑  收藏  举报