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';
}
posted @ 2024-12-25 08:37  EssnSlaryt  阅读(17)  评论(0编辑  收藏  举报