The 2025 ICPC Asia Shenyang Regional Contest

Preface

上周的 ICPC 沈阳,只能说每年赛站博弈都大失败,去年的南京和今年的沈阳看来强度是真低啊

VP 的时候因为有场外因素所以没被现场的榜带歪,很早写过了 AG 两个本质铜牌题后发现在现场就稳 Au 了

最后 DF 双开结果因为我的 F 用了一种不太好写的做法导致没调完,发现现场 F 反而过了一车,十分神秘


A. 平方王国

感觉类似的套路在今年牛客多校就见过,不知道现场为啥没没人写

二分答案的分子 \(M\),枚举有多少对 \(i,j\) 满足 \((i+\frac{b}{a})^2-(j+\frac{b}{a})^2\le \frac{M}{a}\)

用平方差公式把式子拆开得:

\[(i+j+\frac{2b}{a})(i-j)\le \frac{M}{a}\\ \]

枚举 \(d=i-j\),不难发现此时 \(j\le \min(n-d,\lfloor\frac{M-2bd-ad^2}{2*a*d}\rfloor)\)

不难发现大力枚举 \(d\) 的话复杂度是 \(\min(\sqrt n,\sqrt k)\) 的,整体复杂度可以接受

注意 \(M\) 的上界可能要到 \(10^{30}\) 级别,需要开 __int128

#include<cstdio>
#include<iostream>
#include<algorithm>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
typedef __int128 i128;
int n,k,a,b;
inline int calc(const i128& M)
{
    i128 res=0;
    for (int d=1;d<=n-1;++d)
    {
        i128 tmp=min((i128)n-d,(M-2*b*d-a*d*d)/(2*a*d));
        if (tmp<=0) return res;
        if ((res+=tmp)>=k) return res;
    }
    return res;
}
inline void write(const i128& x)
{
    if (x<10) return (void)(putchar(x+'0'));
    write(x/10); putchar(x%10+'0');
}
signed main()
{
    scanf("%lld%lld%lld%lld",&n,&k,&a,&b);
    i128 l=0,r=1e30,ans;
    while (l<=r)
    {
        i128 mid=(l+r)/2;
        if (calc(mid)>=k) ans=mid,r=mid-1; else l=mid+1;
    }
    i128 g=__gcd(ans,(i128)a);
    write(ans/g); putchar(' '); write(a/g);
    return 0;
}

B. 出 Bug 的绘画软件 I

考虑枚举最后一共用了 \(x\) 层 layer,不难发现可以对于每种非 \(0\) 颜色贪心排布

出现次数最多的颜色就用最上层的 layer,额外代价为 \(0\);出现次数第二多的颜色就用次上层的 layer,额外代价为 \(b\);依此类推

每个颜色还可以直接用 \(a\) 的代价涂上,最后对于颜色 \(0\),每个都必须花 \(x\times b\) 的贡献把上面的所有 layer 变透明

#include<cstdio>
#include<iostream>
#include<algorithm>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
const int N=505;
int t,n,m,a,b,cnt[N*N];
signed main()
{
    for (scanf("%lld",&t);t;--t)
    {
        scanf("%lld%lld%lld%lld",&n,&m,&a,&b);
        for (RI i=0;i<=n*m;++i) cnt[i]=0;
        for (RI i=1;i<=n*m;++i)
        {
            int x; scanf("%lld",&x);
            ++cnt[x];
        }
        sort(cnt+1,cnt+n*m+1,greater <int>());
        static int sfx[N*N]; sfx[n*m+1]=0;
        for (RI i=n*m;i>=1;--i)
        sfx[i]=sfx[i+1]+a*cnt[i];
        int ans=sfx[1],pfx=0;
        for (RI i=1;i<=n*m;++i)
        {
            pfx+=min(a,(i-1)*b)*cnt[i];
            // printf("%lld %lld %lld\n",i,pfx,sfx[i+1]);
            ans=min(ans,pfx+sfx[i+1]+i*b*cnt[0]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

F. 友谊地久天长

简单手玩会发现可以分以下三种情况:

  1. \(x,y\) 不相邻,此时直接让这两个点没有出边即可;
  2. \(x,y\) 相邻且在一个最小环上,此时只需要把最小环定向,剩余的边都指向最小环即可(无关的边随便定向);
  3. 可以从边 \(x\to y\)(或 \(y\to x\))出发,走到某个最小环上,满足该路径上没有环;

由于图中只要存在环就一定能找到最小环,VP 的时候编了一个基于 DFS 生成树的方法找最小环

大致思路就是先找到树上有返祖边连接的的两个点,然后通过其它返祖边把两点间的路径尽可能缩短

然而在实现时有一些神秘的细节没讨论清楚,赛后反思应该直接去写好写的 \(O(n^3)\) 弗洛伊德找最小环的

#include<cstdio>
#include<iostream>
#include<vector>
#include<assert.h>
#define RI register int
#define CI const int&
using namespace std;
const int N=305;
int t,n,m,a,b,x[N*N],y[N*N],nei[N][N],key[N],vis[N],fa[N],dep[N],dir[N][N];
vector <int> E[N],T[N],up[N],dn[N]; bool flag;
inline void print(int has_dir=0)
{
    puts("Yes");
    static int out[N];
    for (RI i=1;i<=n;++i) out[i]=0;
    for (RI i=1;i<=m;++i)
    {
        if (dir[x[i]][y[i]]||dir[y[i]][x[i]])
        {
            if (dir[y[i]][x[i]]) swap(x[i],y[i]);
        } else
        {
            assert((key[x[i]]&key[y[i]])==0);
            if (key[x[i]]) swap(x[i],y[i]);
        }
        ++out[x[i]];
        printf("%d %d\n",x[i],y[i]);
    }
    // for (RI i=1;i<=n;++i)
    // if (has_dir&&key[i]) assert(out[i]==1);
}
inline void DFS(CI now)
{
    vis[now]=1;
    for (auto to:E[now])
    {
        if (vis[to])
        {
            if (to==fa[now]) continue;
            dn[to].push_back(now);
            up[now].push_back(to);
            continue;
        }
        dep[to]=dep[now]+1; fa[to]=now;
        T[now].push_back(to); DFS(to);
    }
}
inline void label(vector <int>& circle,int x,CI y,bool not_single=0)
{
    int st_x=x;
    while (x!=y)
    {
        circle.push_back(x);
        int nxt=fa[x];
        for (auto z:up[x])
        {
            if (not_single&&x==st_x&&z==y) continue;
            if (dep[z]>=dep[y]&&dep[z]<dep[nxt]) nxt=z;
        }
        x=nxt;
    }
    circle.push_back(y);
    // assert((int)circle.size()>=3);
}
inline void travel(CI now)
{
    if (flag) return;
    if (!dn[now].empty())
    {
        // puts("Case 3");
        int x=now;
        while (fa[x])
        {
            key[x]=1;
            dir[fa[x]][x]=1;
            x=fa[x];
        }
        key[x]=1;
        vector <int> circle;
        label(circle,dn[now].back(),now,1);
        for (auto x:circle) key[x]=1;
        int sz=(int)circle.size();
        for (RI i=0;i<sz;++i)
        dir[circle[i]][circle[(i+1)%sz]]=1;
        print(1);
        flag=1; return;
    }
    for (auto to:T[now])
    {
        travel(to);
        if (flag) return;
    }
}
int main()
{
    for (scanf("%d",&t);t;--t)
    {
        scanf("%d%d%d%d",&n,&m,&a,&b);
        for (RI i=1;i<=n;++i)
        {
            key[i]=0;
            E[i].clear();
            for (RI j=1;j<=n;++j)
            nei[i][j]=dir[i][j]=0;
        }
        for (RI i=1;i<=m;++i)
        {
            scanf("%d%d",&x[i],&y[i]);
            nei[x[i]][y[i]]=nei[y[i]][x[i]]=1;
            E[x[i]].push_back(y[i]);
            E[y[i]].push_back(x[i]);
        }
        if (!nei[a][b])
        {
            // puts("Case 1");
            key[a]=key[b]=1;
            print();
            continue;
        }
        // auto trs=[&](vector <int> vec,CI val)
        // {
        //     vector <int> res={val};
        //     for (auto x:vec)
        //     if (x!=val) res.push_back(x);
        //     return res;
        // };
        // E[a]=trs(E[a],b); E[b]=trs(E[b],a);
        for (RI i=1;i<=n;++i)
        {
            vis[i]=fa[i]=dep[i]=0;
            T[i].clear(); up[i].clear(); dn[i].clear();
        }
        dep[a]=1; vis[a]=1; T[a].push_back(b);
        fa[b]=a; dep[b]=2; DFS(b);
        if (!dn[a].empty())
        {
            // puts("Case 2");
            int id=dn[a][0];
            for (RI i=1;i<(int)dn[a].size();++i)
            if (dep[dn[a][i]]<dep[id]) id=dn[a][i];
            vector <int> circle;
            label(circle,id,b);
            circle.push_back(a);
            for (auto x:circle) key[x]=1;
            // for (auto x:circle) printf("%d ",x); putchar('\n');
            int sz=(int)circle.size();
            for (RI i=0;i<sz;++i)
            dir[circle[i]][circle[(i+1)%sz]]=1;
            print(1);
            continue;
        }
        flag=0; travel(b);
        if (flag) continue;
        for (RI i=1;i<=n;++i)
        {
            vis[i]=fa[i]=dep[i]=0;
            T[i].clear(); up[i].clear(); dn[i].clear();
        }
        dep[b]=1; vis[b]=1; T[b].push_back(a);
        fa[a]=b; dep[a]=2; DFS(a);
        flag=0; travel(a);
        if (!flag) puts("No");
    }
    return 0;
}

G. 碰撞伤害

神秘几何,我题面都没看就被队友秒了

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define LD long double

struct Pt {
    int x, y;
    int crs(const Pt &a) const {return x*a.y-y*a.x;}
    Pt operator+(const Pt &a) const {return Pt{x+a.x, y+a.y};}
    Pt operator-(const Pt &a) const {return Pt{x-a.x, y-a.y};}
    bool operator<(const Pt &a) const {return x!=a.x ? x < a.x : y < a.y;}
};

int area(const vector<Pt> &vec) {
    int ans = 0;
    int sz = vec.size();
    for (int i=0; i<sz; ++i) {
        ans += vec[i].crs(vec[(i+1)%sz]);
    }
    return ans;
}

vector<Pt> conv(vector<Pt> vec) {
    sort(vec.begin(), vec.end());
    vector<Pt> st;
    const auto check = [](const vector<Pt> &st, const Pt &u) {
        const auto back1 = st.back(), back2 = *prev(st.end(), 2);
        return (back1-back2).crs(u-back1) <= 0;
    };
    for (const Pt &u : vec) {
        while (st.size() > 1 && check(st, u)) st.pop_back();
        st.push_back(u);
    }
    int k = st.size();
    vec.pop_back();
    reverse(vec.begin(), vec.end());
    for (const Pt &u : vec) {
        while (st.size() > k && check(st, u)) st.pop_back();
        st.push_back(u);
    }
    st.pop_back();
    return st;
}

vector<Pt> minco(const vector<Pt> &P, const vector<Pt> &Q) {
    vector<Pt> vec;
    int szp = P.size(), szq = Q.size();
    for (int i=0; i<szp; ++i) {
        vec.push_back(P[i]);
        for (int j=1; j<szq; ++j) {
            vec.push_back(P[i]+Q[j]-Q[0]);
        }
    }
    return conv(vec);
}



LD solve() {
    int n, m; cin >> n >> m;
    vector<Pt> P, Q;
    for (int i=0; i<n; ++i) {
        int x, y; cin >> x >> y; P.push_back(Pt{x, y});
    }
    for (int i=0; i<m; ++i) {
        int x, y; cin >> x >> y; Q.push_back(Pt{-x, -y});
    }
    int res1 = area(P) * area(Q);
    vector<Pt> mc = minco(P, Q);
    // for (auto p : mc) printf("(%lld %lld)\n", p.x, p.y);
    int res2 = area(minco(P, Q));
    // printf("res1=%lld res2=%lld\n", res1, res2);
    return 0.5L * res1 / res2;
}

signed main() {
    ios::sync_with_stdio(0); cin.tie(0);
    cout << setiosflags(ios::fixed) << setprecision(12);
    int T; cin >> T; while (T--) cout << solve() << '\n';
    return 0;
}

I. 志愿者模拟器

签到,我题都没看

#include <bits/stdc++.h>

int solved[500][20];
int bc[500];

int main(void) {
    std::ios::sync_with_stdio(false);
    memset(solved, 0, sizeof solved);
    memset(bc, 0, sizeof bc);
    int n; std::cin >> n;
    while(n--) {
        int a, b, c;
        std::cin >> a >> b >> c;
        if(solved[a][b]) { 
            std::cout << "0\n";
        } else {
            solved[a][b] = 1;
            if(c < 240 || bc[a] < 3) std::cout << b << char(10);
            else                     std::cout << "0\n";
            bc[a] += 1;
        }
    }
    return 0;
}

K. 接力跳

注意到只考虑 \(x\) 坐标的和 \(sum_x\),编号为 \(i\) 的青蛙跳过了编号为 \(j\) 的青蛙后 \(sum_x\) 会加上 \(2(x_j-x_i)\)

如果进行一系列的连续跳跃,中间的很多项都会消掉,最后跳的青蛙 \(x_t\) 满足 \(2(x_t-x_s)\) 等于两个局面 \(sum_x\) 的差值

对于 \(y\) 坐标也是同理,因此直接枚举找到那只青蛙即可

#include <bits/stdc++.h>

using llsi = long long signed int;
constexpr int $n = 100005;

llsi X1[$n], Y1[$n], X2[$n], Y2[$n];

int main() {
    std::ios::sync_with_stdio(false);

    int n, s; std::cin >> n >> s;
    llsi X1_sum = 0, Y1_sum = 0, X2_sum = 0, Y2_sum = 0;
    for(int i = 1; i <= n; ++i) {
        std::cin >> X1[i] >> Y1[i] >> X2[i] >> Y2[i];
        X1_sum += X1[i];
        Y1_sum += Y1[i];
        X2_sum += X2[i];
        Y2_sum += Y2[i];
    }

    llsi X_delta = X2_sum - X1_sum, Y_delta = Y2_sum - Y1_sum;
    for(int t = 1; t <= n; ++t) {
        if(X_delta == 2 * (X2[t] - X1[s]) && Y_delta == 2 * (Y2[t] - Y1[s])) {
            std::cout << t << "\n";
            return 0;
        }
    }

    return 1;
}

Postscript

这么看来这场 7 题感觉有手就行啊,那么请问是谁没手呢

posted @ 2025-11-20 16:08  空気力学の詩  阅读(0)  评论(0)    收藏  举报