unfinished

华东理工大学 2026 年程序设计竞赛春季校赛(重现赛)总结

比赛链接题解和题目链接,队友:rabbit_mygo

I

简单题,分别计算两种作物的利润即可。

code:

#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <queue>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
#define ll long long
static char stkk[200];
template<typename T>inline void output(T x){
    if(!x)return putchar('0'),void();
    if(x<0)x = ~x+1,putchar('-');
    int top = 0;
    for(;x;stkk[++top]=x%10^48,x/=10);
    for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
    x = 0;int y = 1;char c = getchar();
    for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
    for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
    x *= y;
}
const int N = 1e5+10;
static int n,m,k;
inline ll cal0(int p,int q,int t){
    return 1ll*(k-1)/t*(q-p);
}
inline ll cal1(int p,int q,int t,int s){
    return k>=t?1ll*((k-t-1)/s+1)*q-p:0;
}
inline void ckmax(ll &x,ll y){
    x<y&&(x=y);
}
inline void solve(){
    readx(n),readx(m),readx(k);
    ll ans = 0;
    int p,q,t,s;
    FOR(i,1,n)readx(p),readx(q),readx(t),ckmax(ans,cal0(p,q,t));
    FOR(i,1,m)readx(p),readx(q),readx(t),readx(s),ckmax(ans,cal1(p,q,t,s));

    output(ans),putchar(10);
}
signed main(){
    int T;
    for(readx(T);T--;solve());
    return 0;
}

F

简单题,分成四个象限求答案即可。

code:

#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <queue>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
#define ll long long
static char stkk[200];
template<typename T>inline void output(T x){
    if(!x)return putchar('0'),void();
    if(x<0)x = ~x+1,putchar('-');
    int top = 0;
    for(;x;stkk[++top]=x%10^48,x/=10);
    for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
    x = 0;int y = 1;char c = getchar();
    for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
    for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
    x *= y;
}
inline int cal1(int x,int y,int tx,int ty){
    if(ty>=y&&tx>x)return 0;
    if(ty<y&&tx>=x)return 1;
    if(ty<=y&&tx<x)return 2;
    if(ty>y&&tx<=x)return 3;
}
inline int cal0(int x,int y,int tx,int ty){
    return x==tx?ty>y?3:1:tx<x?2:0;
}
inline int cal(int p0,int p1){
    return p1>=p0?p1-p0:p1-p0+4;
}
inline void get_c(char &c){
    for(c = getchar();std::isspace(c);c = getchar());
}
inline void solve(){
    int x,y,tx,ty;char c;
    readx(x),readx(y),get_c(c),readx(tx),readx(ty);
    int p1;
    int p0 = c=='E'?0:c=='S'?1:c=='W'?2:3;
    if(x==tx&&y==ty)output(0),putchar(10);
    else if(x==tx||y==ty){
        p1 = cal0(x,y,tx,ty);
        output(cal(p0,p1)),putchar(10);
    }
    else {
        // printf("p0:%d p1:%d\n",p0,p1);
        p1 = cal1(x,y,tx,ty);
        if(p0==0){
            if(p1==0)output(3);
            else if(p1==1)output(1);
            else if(p1==2)output(2);
            else output(3);
        }
        else if(p0==1){
            if(p1==0)output(3);
            else if(p1==1)output(3);
            else if(p1==2)output(1);
            else output(2);
        }
        else if(p0==2){
            if(p1==0)output(2);
            else if(p1==1)output(3);
            else if(p1==2)output(3);
            else output(1);
        }
        else{
            if(p1==0)output(1);
            else if(p1==1)output(2);
            else if(p1==2)output(3);
            else output(3);
        }
        putchar(10);
    }
}
signed main(){
    int T;for(readx(T);T--;solve());
    return 0;
}

G

挺好玩的题。

首先注意到胜者可以写成 \(1+n+km\) 的形式,于是我们做差就可以得到 \(m\) 的倍数。

我们定义胜者为 \(w_i\),而败者为 \(l_i\),于是有 \(km=\gcd_{1\le x<y\le k}(w_x-w_y)\)。现在我们证明 \(m=km\) 是一定不劣的,因为没啥能缩小 \(m\) 范围的方式了。

  • 可以发现加时情况下比分差会落在 \([2,m+1]\) 之间,那 \(m\) 尽可能取大是不劣的。

此时钦定所有胜者中比分最小 \(w_{、min}\) 的没有进行加时,而其他进行加时同样是优的:

  • 我们可以取 \(n=w_{\min}-1\),这样题目里所有的情况都满足了。

这样所有败者都是尽可能成立的了。

所以现在有 \(n=w_{\min}-1, m=\gcd_{1\le x<y\le k}(w_x-w_y)\),现在我们有一个结论:\(\forall i \in [1,n],\ j \in [1,n]\ \gcd\{p_i - p_j\}\)\(\forall i \in [1,n-1]\ \gcd\{p_{i+1} - p_i\}\) 相等。具体怎么来的不证。

然后 \(m,n\) 就都能求了。最后判断方式为:

\[\begin{array}{l} \left\{\begin{matrix} w_i −l_i \in [2,m+1], w_i \ne w_{\min}\\ w_i −l_i \in [2,n+1], w_i = w_{\min}\\ \end{matrix}\right. \end{array} \]

然后就做完了。

code:

#include<bits/stdc++.h>
#define fi first
#define se second
#define int long long
#define pii pair<int,int>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=5e5+10;
int n,m,k,T;
pii a[N];
vector<int> g[N];
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
    cin>>T;
	while(T--){
		int tg=0,mn=LONG_LONG_MAX;
		cin>>k;
        rep(i,1,k){
			cin>>a[i].fi>>a[i].se;
			mn=min(mn,max(a[i].fi,a[i].se));
		}if(k==1) cout<<"YES\n";
		else{
			m=abs(max(a[1].fi,a[1].se)-max(a[2].fi,a[2].se));
			rep(i,2,k-1){
				m=__gcd(m,abs(max(a[i].fi,a[i].se)-max(a[i+1].fi,a[i+1].se)));	
			}n=mn-1;
			if(m!=0){
				rep(i,1,k){
					int tp=max(a[i].fi,a[i].se);
					if(mn==tp){
					    if(abs(a[i].fi-a[i].se)>n+1) tg=1;
					}else{
						if(abs(a[i].fi-a[i].se)>m+1) tg=1;
				    }
	            }
			}
			if(tg==1) cout<<"NO\n";
			else cout<<"YES\n";
	    }   
	}
	return 0;
}

D

简单构造。

发现应该是 \(\frac {n}{\log n}\) 的询问,然后注意一下横向的边是比较特别的,因为这种边只能是单向的,而且两岸的边一定是反向的,也就是说如果要从这个边的左侧走到右侧,一定会经过这两条边固定向右的那条。

考虑用二进制赋值,我们把横向的边 \(29\) 个位一组,把一组中的所有在一侧的横向边标记权值,然后其他边为 \(0\) 就好了。

code:

#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
using namespace std;
const int N=5e5+10;
int n,m,k,T,a[N];
vector<int> g[N];
int ksm(int x,int k){
	int ans=1;
	while(k){
		if(k%2) ans=ans*x;
		x=x*x;k/=2;
	}return ans;
}
signed main(){
    cin>>n;
	rep(i,1,n-1){
		cout<<ksm(2,(i-1)%29)<<" "<<0<<" "<<0<<" "<<0<<'\n';
		fflush(stdout);
	}rep(i,1,(n+28)/29){
		cout<<"? N "<<(i-1)*29+1<<" S "<<min(n,i*29+1)<<'\n';
		fflush(stdout);
		int x;cin>>x;
		rep(j,1,29){
			int tp=x%2;x/=2;
			if(tp==0) a[j+(i-1)*29]=1;
		}
	}cout<<"! ";
	fflush(stdout);
	rep(i,1,n-1){
		if(a[i]==0) cout<<"O";
		else cout<<"I";
		fflush(stdout);
	}fflush(stdout);
	return 0;
}

H

计数题。

首先发现置换其实可以看成连边,然后这样会构成很多个环,具体为什么可以看这个视频:link。然后我们发现交换两个数其实就是换两条边,而且答案其实就是这些换的 \(\text{lcm}\)。于是我们分类讨论一下。

  1. 环内两数交换,会导致换分裂,但是所有环内的两个值,如果相隔一样的距离,那么交换后的结果都是一样的。所以可以发现总共本质只有 \(O(n)\) 种答案。
  2. 环外两数交换,会导致两环结合,但是总共只有 \(n\) 个点,所以总共也只有 \(O(\sqrt n)\) 种环长,所以暴力组合也只有 \(O(n)\) 种可能。

最后处理一下 \(\text{lcm}\)。就是用一些桶存下每个素数的最大值,然后要判断的时候用栈回溯和暴力拆分数字就好了,比较难写。

code:

#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <vector>
#include <set>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
#define ve std::vector
#define pb push_back
#define ll long long
#define int long long
static char stkk[200];
template<typename T>inline void output(T x){
    if(!x)return putchar('0'),void();
    if(x<0)x = ~x+1,putchar('-');
    int top = 0;
    for(;x;stkk[++top]=x%10^48,x/=10);
    for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
    x = 0;int y = 1;char c = getchar();
    for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
    for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
    x *= y;
}
const int N = 1e5+10,mo = 998244353;
static int n,a[N];
struct node{
    int blp,v;
};
static ve<node> p[N];
inline void init_fac(int x,ve<node> &p){
    for(int i = 2;i*i <= x;++i){
        if(!(x%i)){
            int v = 1;
            for(;!(x%i);x/=i,v*=i);
            p.pb((node){i,v});
        }
    }
    if(x!=1){
        p.pb((node){x,x});
    }
}
inline int ksm(int a,int b){
    int c = 1;for(;b;b>>=1,a = 1ll*a*a%mo)if(b&1)c = 1ll*c*a%mo;return c;
}
static int inv[N];
inline void init(int n){
    //p,inv
    FOR(i,1,n)init_fac(i,p[i]);
    inv[0] = 1;
    FOR(i,1,n)inv[i] = ksm(i,mo-2);
    inv[0] = 1;
}
static int f[N],sz[N];
int find(int x){
    return x==f[x]?x:f[x]=find(f[x]);
}
inline void merge(int x,int y){
    // printf("x:%d y:%d\n",x,y);
    if((x=find(x))==(y=find(y)))return;
    f[y] = x,sz[x]+=sz[y];
}
struct node1{
    int v;
    bool operator<(const node1& A)const{
        return v>A.v;
    }
};
static std::multiset<node1> s[N];
static int ans;
inline void del_s(int l){
    if(!s[l].empty()){
        int x = (*s[l].begin()).v;
        ans = 1ll*ans*inv[x]%mo;
    }
}
inline void add_s(int l){
    if(!s[l].empty()){
        int x = (*s[l].begin()).v;
        ans = 1ll*ans*x%mo;
    }
}
inline void ad_v(int x){
    for(auto &t:p[x]){
        del_s(t.blp);
        s[t.blp].insert((node1){t.v});
        add_s(t.blp);
    }
}
inline void dl_v(int x){
    for(auto &t:p[x]){
        del_s(t.blp);
        auto tmp = s[t.blp].find((node1){t.v});
        // if(tmp==s[t.blp].end())puts("Err"),exit(0);
        s[t.blp].erase(s[t.blp].find((node1){t.v}));
        add_s(t.blp);
    }
}
static int ct[N],xx[N],tp;
inline void solve(){
    //init f,tp,ct,s
    FOR(i,1,n)
    tp = 0;FOR(i,1,n)ct[i] = 0;
    FOR(i,1,n)s[i].clear();
    //rd
    readx(n);
    FOR(i,1,n)f[i] = i,sz[i] = 1;;
    // FOR(i,1,n)printf("i:%d find:%d\n",i,find(i));
    FOR(i,1,n)readx(a[i]),merge(i,a[i]);
    // FOR(i,1,n)printf("i:%d find:%d\n",i,find(i));
    // //merge,get ct
    FOR(i,1,n)if(i==find(i)){
        // printf("i:%d sz:%d\n",i,sz[i]);
        ++ct[sz[i]],xx[++tp] = sz[i];
    }
    //pre ans
    ans = 1;
    FOR(i,1,n)if(ct[i]){
        FOR(j,1,ct[i])ad_v(i);
    }
    int fnl = 0;
    //get in
    FOR(i,1,n)if(ct[i]){
        dl_v(i);
        FOR(x,1,i-1){
            ad_v(x),ad_v(i-x);
            // printf("i:%d ct:%d ans:%d\n",i,ct[i],ans);
            fnl = (0ll+fnl+1ll*i*ct[i]%mo*ans)%mo;
            dl_v(x),dl_v(i-x);
        }
        ad_v(i);
    }
    //get xx,get ou
    std::sort(xx+1,xx+1+tp);tp = std::unique(xx+1,xx+1+tp)-xx-1;
    FOR(i,1,tp){
        FOR(j,1,tp){
            if(i==j&&ct[xx[i]]<=1)continue;
            dl_v(xx[i]),dl_v(xx[j]);
            ad_v(xx[i]+xx[j]);
            fnl = (0ll+fnl+1ll*ct[xx[i]]*xx[i]%mo
            *(ct[xx[j]]-(i==j))%mo*xx[j]%mo*ans%mo)%mo;
            dl_v(xx[i]+xx[j]);
            ad_v(xx[i]),ad_v(xx[j]);
        }
    }
    fnl = 1ll*fnl*ksm(2,mo-2)%mo;
    output((0ll+fnl+mo)%mo),putchar(10);
}
signed main(){
    //init
    init(N-5);
    //solve
    int T;for(readx(T);T--;solve());
    return 0;
}
posted @ 2026-05-10 15:01  NeeDna  阅读(26)  评论(1)    收藏  举报