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;
}
}