Rayan Programming Contest 2024 - Selection (Codeforces Round 989, Div. 1 + Div. 2) A~F

Rayan Programming Contest 2024icon-default.png?t=O83Ahttps://codeforces.com/contest/2034

A. King Keykhosrow's Mystery

用 [a,b] 表示a, b的最小公倍数,保守估计有:m \le [a,b] \le a\cdot b < 10^6 ,加上测试点最坏计算量不到 10^8  ,故直接枚举即可。

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define rep(i,a,b) for (int i=a;i<b;++i)
#define per(i,a,b) for (int i=a;i>b;--i)
#define se second 
#define fi first
#define endl '\n'
#define all(x) (x).begin(),(x).end()
#define pii pair<int,int>
#define pli pair<LL,int>
#define MEM(a,x) memset(a,x,sizeof(a))
inline int Ls(int p){return p<<1;}
inline int Rs(int p){return p<<1|1;}
typedef long long LL;
typedef unsigned long long ULL;
typedef double db;
using namespace std;
const int N=2e5+10,M=1e9+87;

inline void Solve()
{
	int a,b;
	cin>>a>>b;
	int m=min(a,b);
	while(m%a!=m%b) ++m;
	cout<<m<<endl;
}
int main()
{
	#ifndef ONLINE_JUDGE
		freopen("1.in","r",stdin);
		freopen("1.out","w",stdout);
	#endif
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int _=1;
	cin>>_;
	while(_--){
		Solve();
	}
	return 0;
}

B. Rakhsh's Revival

贪心算法,每次遇到连续 m 个 0 即使用往后填补 k 位。显然如果把填补往前移可能让后面更少的 0 变成 1,效果会更差。

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define rep(i,a,b) for (int i=a;i<b;++i)
#define per(i,a,b) for (int i=a;i>b;--i)
#define se second 
#define fi first
#define endl '\n'
#define all(x) (x).begin(),(x).end()
#define pii pair<int,int>
#define pli pair<LL,int>
#define MEM(a,x) memset(a,x,sizeof(a))
inline int Ls(int p){return p<<1;}
inline int Rs(int p){return p<<1|1;}
typedef long long LL;
typedef unsigned long long ULL;
typedef double db;
using namespace std;
const int N=2e5+10,M=1e9+87;

inline void Solve()
{
	int n,m,k;
	string s;
	cin>>n>>m>>k>>s;
	int ans=0,cnt=0;
	rep(i,0,n){
		if(s[i]=='1'){
			cnt=0;
			continue;
		}
		++cnt;
		if(cnt==m){
			++ans;
			i=i+k-1;
			cnt=0;
		}
	}
	cout<<ans<<endl;
}
int main()
{
	#ifndef ONLINE_JUDGE
		freopen("1.in","r",stdin);
		freopen("1.out","w",stdout);
	#endif
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int _=1;
	cin>>_;
	while(_--){
		Solve();
	}
	return 0;
}

C. Trapped in the Witch's Labyrinth

对于确定了方向的点,把边缘上可以直接走出的点标记为 1。把二维转换为一维表示节点,建立与方向相反的有向图,例如 "U" 就是 \text{node}(x-1,y) \to \text{node}(x,y),从标记为 1 的点开始对图做一遍搜索,搜索到的点标记为 1,这样标记为 1 的点就表示能走出的点。

对于没有确定方向的点,如果周围四个点都被标记为 1,说明也是能走出去的点;否则可以把方向指定到不能走出的点。

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define rep(i,a,b) for (int i=a;i<b;++i)
#define per(i,a,b) for (int i=a;i>b;--i)
#define se second 
#define fi first
#define endl '\n'
#define all(x) (x).begin(),(x).end()
#define pii pair<int,int>
#define pli pair<LL,int>
#define MEM(a,x) memset(a,x,sizeof(a))
#define lowbit(x) ((x)&-(x))
inline int Ls(int p){return p<<1;}
inline int Rs(int p){return p<<1|1;}
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
const int N=1e3+10,M=1e6+10;
int n,m;
char ch[N][N];
bool vis[M],f[M];
int h[M],ne[M],e[M];
class Graph{
public:
    int n,idx=0;
    void add(int a,int b){
        e[++idx]=b; ne[idx]=h[a]; h[a]=idx;
    }
    void dfs(int u){
        if(vis[u]) return;
        vis[u]=1; f[u]=1;
        for(int i=h[u];i;i=ne[i]){
            int v=e[i];
            dfs(v);
        }
    }
    void debug(){
    	rep(i,1,n+1){
    		cout<<f[i]<<" ";
    		if(i%m==0) cout<<endl;
    	}
    }
    void clear(){
        idx=0;
        rep(i,1,n+1) h[i]=f[i]=vis[i]=0;
    }
};
bool inrange(int x,int y){
    if(x>=1&&x<=n&&y>=1&&y<=m) return true;
    else return false;
}
int xy2n(int x,int y)
{
    return (x-1)*m+y;
}
bool che(Graph &g,int x,int y)
{
    int dx[]={0,0,-1,1},dy[]={-1,1,0,0};
    rep(i,0,4){
        int nx=x+dx[i],ny=y+dy[i];
        if(inrange(nx,ny) && !f[xy2n(nx,ny)]) return true;
    }
    return false;
}
inline void Solve()
{
    Graph g;
    cin>>n>>m;
    g.n=n*m;
    rep(i,1,n+1) rep(j,1,m+1){
        char c; cin>>c;
        ch[i][j]=c;
        int fr_x,fr_y,to=xy2n(i,j);
        if(c=='?') continue;
        if(c=='U') fr_x=i-1, fr_y=j;
        else if(c=='D') fr_x=i+1, fr_y=j;
        else if(c=='L') fr_x=i, fr_y=j-1;
        else if(c=='R') fr_x=i, fr_y=j+1;
        if(inrange(fr_x,fr_y)){
            g.add(xy2n(fr_x,fr_y),to);
        }else f[to]=1;
    }
    rep(i,1,g.n+1) if(f[i]) g.dfs(i);
    int cnt=0;
    rep(i,1,g.n+1) if(f[i]) cnt+=1;
    rep(i,1,n+1) rep(j,1,m+1){
        if(ch[i][j]!='?') continue;
        if(!che(g,i,j)) ++cnt;
    }
    cout<<g.n-cnt<<endl;
    g.clear();
}
int main()
{
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int _=1;
    cin>>_;
    while(_--){
        Solve();
    }
    return 0;
}

D. Darius' Wisdom

操作就是把相差为 1 的两个数交换,而且可以确定最终的状态是 00...111...222 的形式,从前往后遍历原数组,通过交换操作转换为最终状态,有下面两种情况:

(1)最终状态为 0 的位置,如果当前为 1,把 1 和当前最右端的 0 交换;如果当前为 2,先把 2 和最右端的 1 交换,再把 1 和最右端的 0 交换。

(2)最终状态为 1 的位置,此时 0 已经全部到了正确位置。如果当前为 2 ,与最右段的 1 交换即可。

考虑构造交换次数最多的数据,对于第 2 个操作,一次交换就可以把 1 和 2 都移动到正确位置上;第一个操作种如果出现 1 ,一次交换可以确定 0,只有出现 2 的时候 2 次才能把 0 移动到正确位置。最坏的数据可以把 2 放在最前面,中间插入一个 1,把 0 放在最后面。

打表可以发现这种算法最坏情况就是出现前面 x 个 2,中间一个 1,最后有 x 个 0 的情况,此时交换次数为 n。

模拟上述过程即可,方法有很多种,这里使用堆维护数字的位置。

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define rep(i,a,b) for (int i=a;i<b;++i)
#define per(i,a,b) for (int i=a;i>b;--i)
#define se second 
#define fi first
#define endl '\n'
#define all(x) (x).begin(),(x).end()
#define pii pair<int,int>
#define pli pair<LL,int>
#define MEM(a,x) memset(a,x,sizeof(a))
#define lowbit(x) ((x)&-(x))
inline int Ls(int p){return p<<1;}
inline int Rs(int p){return p<<1|1;}
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
const int N=2e5+10;
int a[N];
void Mov(int i,set<int>&s,set<int>&d,vector<pii>&otp)
{
	int r=*d.rbegin();
	s.emplace(r); s.erase(i);
	d.emplace(i); d.erase(r);
	swap(a[i],a[r]);
    otp.push_back({i,r});
}
inline void Solve()
{
    int n,c[3]={0};
    set<int>st[3];
    cin>>n;
    rep(i,1,n+1){
        cin>>a[i];
        ++c[a[i]];
        st[a[i]].emplace(i);
    }
    vector<pii>otp;
    rep(i,1,c[0]+1){
        if(a[i]==0) continue;
        else if(a[i]==1){
        	Mov(i,st[1],st[0],otp);
        }else{
        	Mov(i,st[2],st[1],otp);
        	Mov(i,st[1],st[0],otp);
        }
    }
    rep(i,1+c[0],c[0]+c[1]+1){
        if(a[i]==1) continue;
        Mov(i,st[2],st[1],otp);
    }
    cout<<otp.size()<<endl;
    for(auto it:otp) cout<<it.fi<<" "<<it.se<<endl;
}
int main()
{
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int _=1;
    cin>>_;
    while(_--){
        Solve();
    }
    return 0;
}

E. Permutations Harmony

总共 k 个排列数字总和为 \frac{n(n+1)}{2} \cdot k ,所以每一列的和一定为 \frac{k(n+1)}{2}

如果 k 为偶数,可以成对的构造排列 p_i 和 n+1-p_i ,使得每两组每列和都是 n+1 。

如果 k 为奇数, 要求 n 必须为奇数,否则列和不是整数。k = 1 时显然必须 n = 1,其他情况可以先构造出 3 个数组使得它们的每一列和相等:

p_1= \{1,2,3,...,2k,2k+1 \} \\ p_2= \{k+1,k+2,k+3,...,1,2 \} \\ p_3= \{2k+1,2k-1,2k-3,...,k+2,k \}

例如 n = 5:

p_1= \{1,2,3,4,5 \} \\ p_2= \{3,4,5,1,2 \} \\ p_3= \{5,3,1,4,2 \}

剩下的 k-3 个按照偶数成对构造的方法即可。

最后考虑判重的情况,生成排列时可以按照字典序从小到大枚举,成对的两个排列在排列字典序中是对称的,所以只需要枚举到中间的排列即可,只需要在 k 为奇数的情况下避免和构造好的 3 个排列重复。

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define rep(i,a,b) for (int i=a;i<b;++i)
#define per(i,a,b) for (int i=a;i>b;--i)
#define se second 
#define fi first
#define endl '\n'
#define all(x) (x).begin(),(x).end()
#define pii pair<int,int>
#define pli pair<LL,int>
#define MEM(a,x) memset(a,x,sizeof(a))
#define lowbit(x) ((x)&-(x))
inline int Ls(int p){return p<<1;}
inline int Rs(int p){return p<<1|1;}
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
const int N=1e5+10;
int n,k;
void cal(set< vector<int> >&ans,set< vector<int> >&st)
{
    vector<int>a,b(n);
    rep(i,1,n+1) a.emplace_back(i);
    do{
        if(ans.size()>=k) break;
        rep(i,0,n) b[i]=n+1-a[i];
        if(a>=b) break;
        if(!st.count(a) && !st.count(b)) ans.emplace(a), ans.emplace(b);
    }while(next_permutation(all(a)));
}
inline void Solve()
{
    cin>>n>>k;
    set< vector<int> >ans,st;
    if(k&1){
        if(n%2==0){
            cout<<"NO"<<endl;
            return;
        }
        vector<int>a,b,c;
        rep(i,1,n+1) a.emplace_back(i);
        rep(i,n/2+1,n+1) b.emplace_back(i); rep(i,1,n/2+1) b.emplace_back(i);
        int tot=(n+1)/2*3;
        rep(i,0,n) c.emplace_back(tot-a[i]-b[i]);
        ans.emplace(a); ans.emplace(b); ans.emplace(c);
        st.emplace(a); st.emplace(b); st.emplace(c);
        cal(ans,st);
    }else{
        cal(ans,st);
    }
    if(ans.size()==k){
        cout<<"YES"<<endl;
        for(auto vc:ans){
            for(int it:vc) cout<<it<<" ";
            cout<<endl;
        }
    }else cout<<"NO"<<endl;
}
int main()
{
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int _=1;
    cin>>_;
    while(_--){
        Solve();
    }
    return 0;
}

F. Khayyam's Royal Decree 

触发宝箱(chest)的条件 (r_i, b_i) 相当于背包中的状态为 (r^{'}_i, b^{'}_i) ,(r^{'}_i=n-r_i, b^{'}_i=m-b_i) 在游戏进行过程中 r^{'}_i, b^{'}_i  都是递增的,所以状态 (r^{'}_i, b^{'}_i) 能转换到状态 (r^{'}_j, b^{'}_j) 当且仅当有:r^{'}_i \le r^{'}_j , b^{'}_i \le b^{'}_j ,把状态先按照 r^{'}_i 从小到大排序,状态转换时只需考虑 b^{'}_i 即可。从  (r^{'}_i, b^{'}_i)(r^{'}_j, b^{'}_j) 的方案数就是 \binom{r^{'}_j-r^{'}_i+b^{'}_j-b^{'}_i}{r^{'}_j-r^{'}_i}

考虑所有情况下从前往后计算第 i 个条件触发时对答案的贡献,为了方便可以添加最终状态 (n, m) 也做为条件,它的贡献相当于原本不算奖励的值。第 i 个条件发生一次会产生 (2\cdot r^{'}_i+b^{'}_i) 的贡献,再计算方案数:从初始 (0,0) 到 (r^{'}_i, b^{'}_i) 有 \binom{r^{'}_i+b^{'}_i}{r^{'}_i} 种方案,还要乘上 i 后面有多少种方案经过了 i(这里是计算后面状态中出现了 i 状态的情况),用 f[i] 表示该方案数,可以得到递推式:

f[i]=\sum^k_{j=i+1} \binom{r^{'}_j-r^{'}_i+b^{'}_j-b^{'}_i}{r^{'}_j-r^{'}_i} \cdot f[j]

综上第 i 个条件总贡献为:(2\cdot r^{'}_i+b^{'}_i)\cdot \binom{r^{'}_i+b^{'}_i}{r^{'}_i} \cdot f[i]

期望就是所有贡献加起来再除以总方案数:\binom{n+m}{n} 

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define rep(i,a,b) for (int i=a;i<b;++i)
#define per(i,a,b) for (int i=a;i>b;--i)
#define se second 
#define fi first
#define endl '\n'
#define all(x) (x).begin(),(x).end()
#define pii pair<int,int>
#define pli pair<LL,int>
#define MEM(a,x) memset(a,x,sizeof(a))
#define lowbit(x) ((x)&-(x))
inline int Ls(int p){return p<<1;}
inline int Rs(int p){return p<<1|1;}
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
const int MOD=998244353;
const int N=5010,M=4e5+10;
pii a[N];
int f[N];
int fac[M],inv[M];
class Math{
public:
    Math(){
        init();
    }
    int qp(int a,int b,int m=MOD){
        int res=1;
        while(b){
            if(b&1) res=1ll*res*a%m;
            a=1ll*a*a%m;
            b>>=1;
        }
        return res%m;
    }
    int C(int a,int b,int m=MOD){
        if(a<b || a<0 || b<0) return 0;
        return 1ll*fac[a]*inv[b]%m*inv[a-b]%m;
    }
    int invC(int a,int b,int m=MOD){
        if(a<b || a<0 || b<0) return 0;
        return 1ll*inv[a]*fac[b]%m*fac[a-b]%m;
    }
    int mht_dis(pii a,pii b){
        return b.fi-a.fi+b.se-a.se;
    }
    void init(){
        fac[0]=inv[0]=1;
        rep(i,1,M) fac[i]=1ll*fac[i-1]*i%MOD, inv[i]=1ll*inv[i-1]*qp(i,MOD-2)%MOD;
    }
}mth;
int calc(int i,int j)
{
    return mth.C(mth.mht_dis(a[i],a[j]),a[j].fi-a[i].fi);
}
void Clear(int k)
{
    rep(i,1,k+1) f[i]=0;
}
inline void Solve()
{
    int n,m,k;
    cin>>n>>m>>k;
    rep(i,1,k+1){
    	cin>>a[i].fi>>a[i].se;
    	a[i].fi=n-a[i].fi; a[i].se=m-a[i].se;
    }
    a[++k]={n,m};
    sort(a+1,a+1+k);
    f[k]=1;
    per(i,k-1,0) rep(j,i+1,k+1){
        f[i]+=1ll*f[j]*calc(i,j)%MOD;
        f[i]%=MOD;
    }
    int ans=0;
    rep(i,1,k+1){
        int res=1ll*(2*a[i].fi+a[i].se)*calc(0,i)%MOD*f[i]%MOD;
        ans=(ans+res)%MOD;
    }
    ans=1ll*ans*mth.invC(n+m,m)%MOD;
    cout<<ans<<endl;
    Clear(k);
}
int main()
{
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int _=1;
    cin>>_;
    while(_--){
        Solve();
    }
    return 0;
}

Update 1

F 题中采用计算 i 贡献的方法并不会算重复。计算第 i 个贡献时第一部分是从  (0,0) 到 (r^{'}_i, b^{'}_i) 的方案,这些方案可能包括了 i 前面部分的状态  (r^{'}_j, b^{'}_j) ,这些状态的贡献还没有计算。 f[i]  的意义就是计算后续有多少种包含了前面 i 的方案数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值