The 2023 ICPC Xi'an Invitational Programming Contest / 2023 ICPC 全国邀请赛 西安
G. Permutation
直接输出 1 到 n
J. Target
一开始想了半天可不可以找一个中间值,使得 \(a,b\) 都一定能变成中间值
后来发现,可以先把 \(a\) 变成 \(0\),之后 \(a\) 的每次操作实际就是,在 \(a\) 的二进制位表示下,给小数的每一位赋值 \(1\) 或 \(0\)
二进制逼近 \(b\) 即可
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
using ll=long long;
using pii=pair<int,int>;
const ll inf = 1e18;
const int mod = 1e9+7;
const double eps1=1e-6;
const double eps=1e-4;
void solve(){
double a,b,s=0.5;
cin>>a>>b;
int cnt=0;
while(a>=eps1 && cnt<50){
cnt++;
a=a*s;
cout<<1;
}
vector<int> ans;
while(b>=eps1){
if(b>=s){
ans.push_back(2);
b-=s;
}
else ans.push_back(1);
s=0.5*s;
}
for(int i=ans.size()-1;i>=0;i--){
cout<<ans[i];
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
// cin>>ct;
while(ct--) solve();
return 0;
}
A. Alice and Bob
VP 时还没看题队友就把思路出完了,给我讲了个大致的排列方法,我只需要推一下组合数学式子+写代码就 A 了。好爽
\(Alice\) 先手,对于一个固定的 \(p[1]\),如果 \(p[1,p1]\) 中,的最小值小于 \(p[1]\),则把最小值放到最前面,先手一定赢
如果最小值大于 \(p[1]\),则先手一定输
证明是容易手搓出来的
然后就是组合数学,可以直接算输的情况,对于一个固定的 \(p[1]=i\),先手输的情况数为:\(C(n-i,i-1)*(i-1)!*(n-i)!\)
\(C(n-i,i-1)\) 表示从 \(n-i\) 个比 \(i\) 的数中,选 \(i-1\) 个的组合数,后面两个阶乘是这两部分的排列
枚举 \(i\) 累加答案即可
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
using ll=long long;
using pii=pair<int,int>;
const ll inf = 1e18;
const int mod = 998244353;
const int N=1e7;
vector<int> fact(N+1),infact(N+1);
int qmi(int a,int b,int p){
int res=1;
while(b){
if(b&1) res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
void init(){
fact[0]=infact[0]=1;
for(int i=1;i<=N;i++){
fact[i]=fact[i-1]*i%mod;
}
infact[N]=qmi(fact[N],mod-2,mod);
for(int i=N-1;i>=1;i--){
infact[i]=infact[i+1]*(i+1)%mod;
}
}
int C(int a,int b){
if(a<0 || b<0 || a<b) return 0;
return fact[a]*infact[b]%mod*infact[a-b]%mod;
}
void solve(){
int n;
cin>>n;
int ans=0;
for(int i=1;i<=n;i++){
ans+=C(n-i,i-1)*fact[i-1]%mod*fact[n-i]%mod;
ans%=mod;
}
cout<<ans;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
init();
int ct=1;
// cin>>ct;
while(ct--) solve();
return 0;
}
H. Spin the Wheel
VP 的时候还是不够冷静/理性,没有去一点一点考虑所有情况,导致没找出 111111 和数组中没 0 时的情况
首先,可以观察到,在取模的意义下,如果相邻两个数的差存在数值不同的情况,则一定是 -1
因为,每次相加,都会让差值固定 +1
设差值为 \(d\),\(0<=d<n\)
如果 \(d=0\):
- 如果数组是全零,则答案为 \(0\)
- 否则答案为 \(n+1\),手搓一下能找出来。(最小是 \(n\) 次,因为要让公差为 \(n\)
如果 \(d!=0\):
- 如果 \(a[1]\) 是零,则直接累加 \(d\) 次 \(0,1,2,3,4,...,n-1\) 即可,答案为 \(d\)
- 否则答案为 \(d+1\),可以先累加 \(d\) 次,然后转一下
E. Merge the Rectangles
从第一次合并到最后一次合并去正向考虑,很难实现。
反向考虑,最后一次合并时,一定是通过一个完全贯穿的边来合并的。
递归的去找这个当前矩形中完全贯穿的边即可
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
using ll=long long;
using pii=pair<int,int>;
const ll inf = 1e18;
const int mod = 1e9+7;
void solve(){
int n,m;
cin>>n>>m;
vector row(n+1,vector<int>(m+1));
auto col=row;
for(int i=1;i<n;i++){
for(int j=1;j<=m;j++){
char ch;
cin>>ch;
row[i][j]=(ch=='1');
row[i][j]+=row[i][j-1];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<m;j++){
char ch;
cin>>ch;
col[i][j]=(ch=='1');
col[i][j]+=col[i-1][j];
}
}
auto dfs=[&](auto dfs,int x1,int y1,int x2,int y2)-> bool {
bool f=0;
for(int i=y1;i<y2;i++){
if(col[x2][i]-col[x1-1][i]==x2-x1+1){
return (dfs(dfs,x1,y1,x2,i) && dfs(dfs,x1,i+1,x2,y2));
}
else f+=col[x2][i]-col[x1-1][i];
}
for(int i=x1;i<x2;i++){
if(row[i][y2]-row[i][y1-1]==y2-y1+1){
return (dfs(dfs,x1,y1,i,y2) && dfs(dfs,i+1,y1,x2,y2));
}
else f+=row[i][y2]-row[i][y1-1];
}
if(f) return 0;
return 1;
};
if(dfs(dfs,1,1,n,m)) cout<<"YES";
else cout<<"NO";
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
// cin>>ct;
while(ct--) solve();
return 0;
}
I. Square
jly 在 qoj 上发的题解:

我的代码是 \(x\) 去找 \(y\) 的,还有分类讨论什么的,其实如果反向思考,让 \(y\) 去找 \(x\),过程是唯一的,很好写
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
using ll=long long;
using pii=pair<int,int>;
const ll inf = 1e18;
const int mod = 1e9+7;
int cal(int i){
double t=sqrt(2*i);
return i+(int)(t+1.5);
}
//计算行号
int row(int val){
int l=1,r=2e9;
while(l<r){
int mid=(l+r)/2;
if(mid*(1+mid)/2>=val) r=mid;
else l=mid+1;
}
return r;
}
void solve(){
int x,y;
cin>>x>>y;
if(y<=x){
cout<<x-y<<endl;
return;
}
auto f=[&](int x)-> int {
int rx=row(x);
int d=rx*(rx+1)/2-x;
int ry=row(y);
int res=ry-rx;
int x2=ry*(ry+1)/2-d;
if(x2<y){
x2=cal(x2);
res++;
}
res+=(x2-y);
return res;
};
//容易证明,最优解一定是二者之一
//1. 先跳在偏移
int ans=f(x);
//2. 先偏移到上一行末尾再跳
if(row(x)>1){
int prer=row(x)-1;
int now=prer*(1+prer)/2;
ans=min(ans,f(now)+x-now);
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--) solve();
return 0;
}

浙公网安备 33010602011771号