2024.12.23 2024 ICPC昆明站
Solved: 6/13
Upsolved: 7/13
Rank: 125
Rank(vp): 232
M. Matrix Construction
题意:构造一个矩阵,使相邻两个数之和两两不重复。
\(n,m\) 有一个为偶数时,令 \(a_{i,j} = (i-1)\times j+1\);
\(n,m\) 均为奇数时,每隔一行循环移位一个元素。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1005;
int a[N][N];
void solve(){
int n,m;
cin>>n>>m;
if(!(m&1)){
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
a[i][j]=(i-1)*m+j;
}
else if(!(n&1)){
for(int i=1;i<=m;++i)
for(int j=1;j<=n;++j)
a[j][i]=(i-1)*n+j;
}
else{
for(int i=1;i<=n;++i){
if(i&1)for(int j=1;j<=m;++j)a[i][j]=(i-1)*m+j;
else{
for(int j=1;j<m;++j)a[i][j]=(i-1)*m+j+1;
a[i][m]=(i-1)*m+1;
}
}
}
cout<<"YES\n";
for(int i=1;i<=n;++i,cout<<'\n')
for(int j=1;j<=m;++j)
cout<<a[i][j]<<' ';
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
int T;
cin>>T;
while(T--)solve();
}
J. Just another Sorting Problem
题意:一个排列,AB 轮流操作。A 每次可以交换任意两个数,B 每次可以交换任意相邻两个数,任意时刻若排列升序则 A 胜,若 A 无法获胜则 B 胜。给定初始排列,求获胜的人。
\(n=2\) 时 A 一定获胜;\(n\geq 4\) 时只要 A 一步无法取胜就不可能取胜了。
\(n=3\) 直接对每种初始排列都特判即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve(){
int n;
string s;
cin>>n>>s;
vector<int> a(n);
for(int& x:a)cin>>x,--x;
if(n==2){
cout<<"Alice\n";
return;
}
if(n==3){
if(a==vector<int>({0,1,2}))cout<<"Alice\n";
else if(a==vector<int>({1,0,2})){
if(s=="Alice")cout<<"Alice\n";
else cout<<"Bob\n";
}
else if(a==vector<int>({2,0,1})){
if(s=="Alice")cout<<"Bob\n";
else cout<<"Alice\n";
}
else if(a==vector<int>({0,2,1})){
if(s=="Alice")cout<<"Alice\n";
else cout<<"Bob\n";
}
else if(a==vector<int>({1,2,0})){
if(s=="Alice")cout<<"Bob\n";
else cout<<"Alice\n";
}
else if(a==vector<int>({2,1,0})){
if(s=="Alice")cout<<"Alice\n";
else cout<<"Bob\n";
}
return;
}
int fl=0;
for(int i=0;i<n;++i)if(i!=a[i])++fl;
if(!fl)cout<<"Alice\n";
else{
if(s=="Alice"&&fl==2)cout<<"Alice\n";
else cout<<"Bob\n";
}
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
int T;
cin>>T;
while(T--)solve();
}
H. Horizon Scanning
题意:给 \(n\) 个点,问中心为原点、半径无线的扇形圆心角至少多大能保证任意情况下都能覆盖至少 \(k\) 个点。
极角排序,答案就是 \(a_{i+k}-a_i\) 的最大值。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=4e5+5;
const double pi=acos(-1);
int n,k,x,y;
double a[N];
void solve(){
cin>>n>>k;
for(int i=1;i<=n;++i){
cin>>x>>y;
a[i]=atan2(y,x);
}
sort(a+1,a+n+1);
for(int i=1;i<=n;++i)a[i+n]=a[i]+2*pi;
double ans=0;
for(int i=1;i<=n;++i)ans=max(ans,a[i+k]-a[i]);
cout<<fixed<<setprecision(12)<<ans<<'\n';
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
int T;
cin>>T;
while(T--)solve();
}
G. GCD
题意:给两个数 \(a,b\),每次可以将 \(a\) 或 \(b\) 减去 \(\gcd(a,b)\),求最少多少次能使 \(a,b\) 减为 \(0\)。\(a\leq 5000,b\leq 10^{18}\)。
注意到至多两次操作即可让 \(a\) 减半,因此答案不会超过 \(2\log a\)。因此限深度暴搜即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
int ans;
void dfs(ll a,ll b,int step){
if(step+1>ans)return;
if(!a||!b){ans=step+1;return;}
ll d=__gcd(a,b);
dfs(a-d,b,step+1);
if(a>d)dfs(a,b-d,step+1);
}
void solve(){
ll a,b;
cin>>a>>b;
if(a>b)swap(a,b);
ans=20,dfs(a,b,0);
cout<<ans<<'\n';
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
int T;
cin>>T;
while(T--)solve();
}
L. Last Chance: Threads of Despair
题意:\(n\) 个人打 \(m\) 个人。我方每人可以对对方的一个人发动一次攻击使两人血量分别减 1。若任意一个人血量减到 \(0\) 以下,他就会自爆并使场上所有人血量减 1。自爆是瞬间发生的,期间不能发动攻击。问能不能将对方所有人全部消灭。
最优策略一定是让血量大于 1 的人先压对方血量,最后让血量等于 1 的人主动攻击后自爆(如果没有,就攻击敌方血量为 1 的人)连锁反应带走敌方全场。
能发起连锁反应当且仅当在起爆之后场上的血量从小到大排序后第 \(i\) 个人血量不超过 \(i-1\)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+5;
int n,m,a[N],b[N];
void solve(){
cin>>n>>m;
ll sum=0;
for(int i=1;i<=n;++i)cin>>a[i],sum+=a[i]>1;
for(int i=1;i<=m;++i)cin>>b[i];
sort(a+1,a+n+1);
sort(b+1,b+m+1);
int i=1,j=1,k=0;
if(a[1]==1)++sum;
while(j<=m){
while(i<=n||j<=m){
if(i<=n&&a[i]-1<=k)++i,++k;
else if(j<=m&&b[j]<=k)++j,++k;
else break;
}
if(j<=m){
if(sum>=b[j]-k){
sum-=b[j]-k,b[j]=k;
++j,++k;
}
else break;
}
}
if(j>m)cout<<"Yes\n";
else cout<<"No\n";
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
int T;
cin>>T;
while(T--)solve();
}
E. Extracting Weights
题意:交互题。给一棵树,根节点点权为 \(0\),其他点点权未知。你可以询问至多 \(n\) 次,每次询问一条长度为 \(k\) 的链上所有点的点权异或和。求所有点的点权,无解直接输出 NO。
假的交互。其实就是看所有长度为 \(k\) 的链能否构成 \(\mathbb{F}_2^{n-1}\) 的一组基。
bitset 高斯消元即可。复杂度 \(O(n^4/w\)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=255,M=4e4+5;
int n,k,x,y;
vector<int> e[N];
void adde(int x,int y){
e[x].push_back(y);
}
int fa[N],dep[N];
vector<int> path(int x,int y){
vector<int> res;
if(dep[x]<dep[y])swap(x,y);
while(dep[x]>dep[y])res.push_back(x),x=fa[x];
while(x!=y){
res.push_back(x),res.push_back(y);
x=fa[x],y=fa[y];
}
res.push_back(x);
return res;
}
void dfs(int u){
for(int v:e[u])if(v^fa[u])fa[v]=u,dep[v]=dep[u]+1,dfs(v);
}
int m=0,u[M],v[M];
bitset<N> g[M];
int qu[N],qv[N],b[N],ans[N];
int main(){
cin>>n>>k;
for(int i=1;i<n;++i)cin>>x>>y,adde(x,y),adde(y,x);
dfs(1);
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j){
auto p=path(i,j);
if(p.size()==k+1){
u[m]=i,v[m]=j;
for(int x:p)if(x>1)g[m].set(x-2);
++m;
}
}
for(int k=0;k<n-1;++k){
int pos=-1;
for(int i=k;i<m;++i)if(g[i][k]){pos=i;break;}
if(pos<0){cout<<"NO\n";return 0;}
if(pos>k)swap(g[k],g[pos]),swap(u[k],u[pos]),swap(v[k],v[pos]);
qu[k]=u[k],qv[k]=v[k];
for(int i=k+1;i<m;++i)if(g[i][k])g[i]^=g[k];
}
cout<<"YES\n";
cout<<"? "<<n-1<<' ';
for(int k=0;k<n-1;++k)cout<<qu[k]<<' '<<qv[k]<<" \n"[k==n-2];
cout.flush();
for(int k=0;k<n-1;++k){
cin>>b[k];
auto p=path(qu[k],qv[k]);
g[k].reset();
for(int x:p)if(x>1)g[k].set(x-2);
}
for(int k=0;k<n-1;++k){
int pos=-1;
for(int i=k;i<n-1;++i)if(g[i][k]){pos=i;break;}
if(pos>k)swap(g[k],g[pos]),swap(b[k],b[pos]);
for(int i=k+1;i<n-1;++i)if(g[i][k])g[i]^=g[k],b[i]^=b[k];
}
for(int k=n-2;k>=0;--k){
ans[k]=b[k];
for(int i=k+1;i<n-1;++i)if(g[k][i])ans[k]^=ans[i];
}
cout<<"! ";
for(int k=0;k<n-1;++k)cout<<ans[k]<<" \n"[k==n-2];
cout.flush();
}
F. Flowers
题意:求 \(\prod_{i=2}^n w(i)\),\(w(n)\) 为 \(n\) 的不同质因子数量。\(n\leq 10^{10}\)。
注意到 \(w(n)\) 不超过 \(10\),因此每隔 \(10^7\) 个数用区间线性筛求一次 \(cnt_k=\sum_{i=2}^m [d(i)=k]\) 然后分段打表即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=1e7+5;
bool np[N];
int pri[N],cnt=0;
void sieve(int n){
for(int i=2;i<=n;++i){
if(!np[i])pri[++cnt]=i;
for(int j=1;j<=cnt&&i*pri[j]<=n;++j){
np[i*pri[j]]=1;
if(!(i%pri[j]))break;
}
}
}
ll t[M];
int w[M],c[11];
void segment_sieve(ll l,ll r){
for(ll i=l;i<=r;++i)t[i-l]=i;
for(int i=1;i<=cnt;++i){
int p=pri[i];
ll j=(l/p)*p;
if(j<l)j+=p;
while(j<=r){
while(!(t[j-l]%p))t[j-l]/=p;
++w[j-l],j+=p;
}
}
for(ll i=l;i<=r;++i){
if(t[i-l]>1)++w[i-l];
++c[w[i-l]];
}
}
ll n,p,m=1e7;
ll qpow(ll x,ll y){
ll r=1;
for(;y;y>>=1){
if(y&1)r=r*x%p;
x=x*x%p;
}
return r;
}
ll P[1000][10]={
// 打表
};
int main(){
cin>>n>>p;
sieve(1e5);
int k=n/m;
ll ans=1;
if(k){
for(int i=2;i<=10;++i)ans=ans*qpow(i,P[k-1][i-1])%p;
}
if(n%m){
segment_sieve(k*m+1,n);
for(int i=2;i<=10;++i)ans=ans*qpow(i,c[i])%p;
}
cout<<ans<<'\n';
}