Rayan Programming Contest 2024https://codeforces.com/contest/2034
A. King Keykhosrow's Mystery
用 表示a, b的最小公倍数,保守估计有:
,加上测试点最坏计算量不到
,故直接枚举即可。
#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" 就是 ,从标记为 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 的两个数交换,而且可以确定最终的状态是 的形式,从前往后遍历原数组,通过交换操作转换为最终状态,有下面两种情况:
(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 个排列数字总和为 ,所以每一列的和一定为
如果 k 为偶数,可以成对的构造排列 和
,使得每两组每列和都是 n+1 。
如果 k 为奇数, 要求 n 必须为奇数,否则列和不是整数。k = 1 时显然必须 n = 1,其他情况可以先构造出 3 个数组使得它们的每一列和相等:
例如 n = 5:
剩下的 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)的条件 相当于背包中的状态为
在游戏进行过程中
都是递增的,所以状态
能转换到状态
当且仅当有:
,把状态先按照
从小到大排序,状态转换时只需考虑
即可。从
到
的方案数就是
考虑所有情况下从前往后计算第 i 个条件触发时对答案的贡献,为了方便可以添加最终状态 也做为条件,它的贡献相当于原本不算奖励的值。第 i 个条件发生一次会产生
的贡献,再计算方案数:从初始
到
有
种方案,还要乘上 i 后面有多少种方案经过了 i(这里是计算后面状态中出现了 i 状态的情况),用
表示该方案数,可以得到递推式:
综上第 i 个条件总贡献为:
期望就是所有贡献加起来再除以总方案数:
#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 个贡献时第一部分是从 到
的方案,这些方案可能包括了 i 前面部分的状态
,这些状态的贡献还没有计算。
的意义就是计算后续有多少种包含了前面 i 的方案数。