题解

难度

A B C D E N 难度1
G H J L 难度2
F M O 难度3
I K 难度4

Problem D

当某个尾号的出现次数大于k时,不可能。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

ll cnt[10000];

void solve() {
    ll n,k;
    cin>>n>>k;
    for(ll i=1;i<=n;++i){
        ll x;
        cin>>x;
        if (++cnt[x%10000] > k) {
            cout<<"Which is my package???";
            return;
        }
    }
    cout<<"This is my package!!!";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t = 1;
    // cin >> t;
    while (t--) {
        solve();
    }

    return 0;
}

Problem E

暴力匹配即可。这里给出手写暴力匹配的代码,也可以直接用库函数。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

string S[105], T[105];

void solve() {
    // 暴力算法
    ll n,m,ans=0;
    cin>>n>>m;
    for (ll i=1;i<=n;++i) cin>>S[i];
    for (ll i=1;i<=m;++i) cin>>T[i];
    for (ll i=1;i<=n;++i){
        ll suc=0;
        for (ll ii=1;ii<=m;++ii) {
            string& s=S[i], &t=T[ii];
            ll l1=s.length(),l2=t.length();
            for(ll j=0;j<=l1-l2;++j){
                // 当前位置是否匹配
                ll ok=1;
                for(ll k=0;k<l2;++k){
                    if(s[j+k]!=t[k]){
                        ok=0;
                        break;
                    }
                }
                if(ok){
                    suc=1;
                    break;
                }
            }
            if(suc) break;
        }
        if (suc) ans+=i;
    }
    cout<<ans;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t = 1;
    // cin >> t;
    while (t--) {
        solve();
    }

    return 0;
}

Problem F

\(f=\frac{(i+1)^6-1}{i},g=\frac{(i+1)^5-1}{i}\),则\(B^{(i)}=(-i-1,f+i+1,-1,g+1)\)
如果观察不出这个结果,可以把\(ad-bc=1,a+b=f,c+d=g\)方程中的\(b\)\(a\)\(f\)表示,\(d\)\(g\)\(c\)表示,代回方程得到\(ag-fc=1\),显然满足exgcd的形式。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

void exgcd(ll a, ll b, ll &x, ll &y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return;
    }
    ll x1, y1;
    exgcd(b, a % b, x1, y1);
    x = y1;
    y = x1 - (a / b) * y1;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    ll a,c;
    cin>>n;
    for(ll i=1+1;i<=n+1;++i){
        ll f=(i*i*i*i*i*i-1)/(i-1), g=(i*i*i*i*i-1)/(i-1);
        exgcd(g,f,a,c);
        cout<<a<<" "<<f-a<<" "<<-c<<" "<<g+c<<"\n";
    }

    return 0;
}

Problem G

规则1每堆石子去掉一个后,剩下的能取\(\lfloor\frac{\sum{(a_i-1)}}{k}\rfloor\)次,规则2则是\(\sum \lfloor{\frac{a_i-1}{k}}\rfloor\)次,根据奇偶性判断即可。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

void solve() {
    ll n,k;
    cin>>n>>k;
    ll sum=0, cnt=0;
    for(ll i=1;i<=n;++i){
        ll x;
        cin>>x;
        sum+=--x;
        cnt+=x/k;
    }
    sum /= k;
    if ((sum&1) || (cnt&1)) {
        cout<<"Alice";
    }
    else{
        cout<<"Bob";
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t = 1;
    // cin >> t;
    while (t--) {
        solve();
    }

    return 0;
}

Problem H

这里介绍\(O(r)\)做法。枚举因数\(p\),考虑\(p\)的倍数在\([l,r]\)内出现的次数,这个次数是\(\lfloor{\frac{r}{p}}\rfloor-\lceil{\frac{l}{p}}\rceil+1\),乘上\(p\)就是\(p\)对答案的贡献,累加\(p\)对答案的贡献即可。本题Hard Version进一步增加数据量,需要根号分治来实现\(O(\sqrt{r})\)的复杂度。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

void solve() {
    ll l,r,ans=0;
    cin>>l>>r;
    for(ll p=1;p<=r;++p){
        ans+=max(0LL, r/p-(l+p-1)/p+1)*p;
    }
    cout<<ans<<'\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t = 1;
    cin >> t;
    while (t--) {
        solve();
    }

    return 0;
}

Problem I

莫队算法维护区间最大出现次数。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const ll N=1e5+5;
ll a[N];
ll id[N]; // 属于哪个块
ll bd;
ll n,q;
#define low(i) ((i-1)*bd+1)
#define high(i) (min(n,i*bd))
struct {
    ll l, r, id;
} qs[N];
ll ans[N];
ll cnt[N], maxcnt[N];
ll pre[N];

ll tree[N<<2]; // 可以直接莫队维护区间最值
#define ls (p<<1)
#define rs (p<<1|1)
void build(ll p, ll pl, ll pr) {
    if(pl==pr){
        tree[p]=a[pl];
        return;
    }
    ll mid=pl+pr>>1;
    build(ls,pl,mid);
    build(rs,mid+1,pr);
    tree[p]=max(tree[ls],tree[rs]);
}
ll query(ll L, ll R, ll p, ll pl, ll pr){
    if(pl>=L && pr<=R){
        return tree[p];
    }
    ll mid=pl+pr>>1;
    ll ret=0;
    if(L<=mid) ret=query(L,R,ls,pl,mid);
    if(R>mid) ret=max(ret,query(L,R,rs,mid+1,pr));
    return ret;
}
void upd(ll& x, ll y, ll& mx){
    // y \in {1, -1}
    maxcnt[x]--;
    x+=y;
    maxcnt[x]++;
    // 超过了原本的最大值
    if(y>0 && x>mx) mx=x;
    // 原本是唯一的最大值
    else if(y<0 && x-y==mx && maxcnt[mx]==0) mx=x; 
}

void solve() {
    cin>>n>>q;
    for(ll i=1;i<=n;++i) cin>>a[i], pre[i]=pre[i-1]+a[i];
    build(1,1,n);
    for(ll i=1;i<=q;++i){
        cin>>qs[i].l>>qs[i].r;
        qs[i].id=i;
    }
    bd=ceil(sqrt(n));
    for(ll i=1;i<=bd;++i){
        for(ll j=low(i);j<=high(i);++j){
            id[j]=i;
        }
    }
    sort(qs+1,qs+1+q,[&](auto x, auto y) {
        if(id[x.l]!=id[y.l])
            return id[x.l]<id[y.l];
        return x.r<y.r;
    });
    ll l,r,cl=1,cr=0,mx=0;
    // mx最大次数 maxcnt维护次数出现的次数
    for(ll i=1;i<=q;++i){
        l=qs[i].l,r=qs[i].r;
        if(l==r){
            ans[qs[i].id]=a[l]*2;
            continue;
        }
        while(cl>l) upd(cnt[a[--cl]],1,mx);
        while(cr<r) upd(cnt[a[++cr]],1,mx);
        while(cl<l) upd(cnt[a[cl++]],-1,mx);
        while(cr>r) upd(cnt[a[cr--]],-1,mx);
        ans[qs[i].id]=mx*query(l,r,1,1,n)+pre[r]-pre[l-1];
    }
    for(ll i=1;i<=q;++i){
        cout<<ans[i]<<'\n';
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t = 1;
    // cin >> t;
    while (t--) {
        solve();
    }

    return 0;
}

Problem J

枚举选出的3个标签,遍历一遍树取最大值。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

ll rnk[26];
ll ct[6];
const ll N=1e5+5;
vector<ll> adj[N];
ll n;
ll color[N], sp[N];

void dfs(ll x, ll tags, ll s, ll& maxv) {
    ll cnt=__builtin_popcountll(tags & ct[color[x]]);
    if(cnt>0) {
        switch (sp[x]) {
        case 'A':
            s+=1;
            break;
        case 'B':
            s*=2;
            break;
        case 'C':
            s-=1;
            break;
        case 'D':
            s=max(cnt,s);
        }
    }
    for(ll& y:adj[x]){
        dfs(y,tags,s,maxv);
    }
    if(adj[x].size()==0){
        maxv=max(maxv,s);
    }
}

void solve() {
    cin>>n;
    for(ll i=2;i<=n;++i){
        ll f;
        cin>>f;
        adj[f].push_back(i);
    }
    for(ll i=1;i<=n;++i){
        char p,q;
        cin>>p>>q;
        color[i]=rnk[p-'A'];
        sp[i]=q;
    }
    ll ans=LLONG_MIN;
    ll tag=0;
    for(ll i=0;i<(1<<6);++i){
        ll cnt=__builtin_popcountll(i);
        if(cnt==3){
            cnt=LLONG_MIN;
            dfs(1,i,0,cnt);
            if(cnt > ans) {
                ans=cnt;
                tag=i;
            }
        }
    }
    vector<ll> out;
    ll j=1;
    while(tag){
        if(tag&1) out.push_back(j);
        tag>>=1;
        ++j;
    }
    for(ll i=0;i<3;++i) cout<<out[i]<<' ';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    rnk['W'-'A']=0;
    rnk['B'-'A']=1;
    rnk['Y'-'A']=2;
    rnk['R'-'A']=3;
    rnk['P'-'A']=4;
    rnk['G'-'A']=5;
    ct[0]=0;
    ct[1]=7; // 012
    ct[2]=54; // 1245
    ct[3]=57; // 0345
    ct[4]=42; //135
    ct[5]=20; //24

    int t = 1;
    // cin >> t;
    while (t--) {
        solve();
    }

    return 0;
}

Problem K

定义\(f[i][j]\)表示从\(i\)走到\(j\),形成回文路径的概率,那么枚举\(i\)的下一个点\(u\)\(j\)和上一个点\(v\),则\(f[i][j]=\sum_u\sum_v P[i][u]*f[u][v]*P[v][j]\),其中\(P[i][j]\)\(i\)的下一个点是\(j\)的概率。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const ll N=105;
ll g[N][N], in[N], out[N];
string s;
ll n,m,S,T;
ll dp[N][N], vis[N][N], inv[300005];
const ll P=998244353;

ll qpow(ll x, ll y) {
    ll ans=1;
    while(y){
        if(y&1) ans=ans*x%P;
        y>>=1;
        x=x*x%P;
    }
    return ans;
}

#define inc(x,y) (x=(x+((y)%P))%P)

ll dfs(ll x, ll y) {
    if(vis[x][y])
        return dp[x][y];
    vis[x][y]=1;
    if(x==y)
        return dp[x][y]=1;
    if(s[x]!=s[y])
        return dp[x][y]=0;
    for(ll i=1;i<=n;++i){
        for(ll j=1;j<=n;++j){
            if(g[x][i] && g[j][y] && s[i]==s[j]) {
                if(i==y || j==x){
                    if(i==y && j==x) {
                        inc(dp[x][y], g[x][y]*inv[out[x]]);
                    }
                }
                else {
                    inc(dp[x][y], g[x][i]*g[j][y]%P*inv[out[x]]%P*inv[out[j]]%P*dfs(i,j));
                }
            }
        }
    }
    return dp[x][y];
}

void solve() {
    cin>>n>>m;
    cin>>s;
    s="$"+s;
    for(ll i=1;i<=m;++i){
        ll u,v;
        cin>>u>>v;
        g[u][v]++;
        in[v]++, out[u]++;
    }
    for(ll i=1;i<=n;++i){
        if(in[i]==0) S=i;
        if(out[i]==0) T=i;
    }
    cout<<dfs(S,T);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    for(ll i=1;i<300005;++i){
        inv[i]=qpow(i,P-2);
    }

    int t = 1;
    // cin >> t;
    while (t--) {
        solve();
    }

    return 0;
}

Problem L

对于一个点P,向量APBPBPCPCPAP的叉积符号全部一样说明P在内部。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

ll x0_,y0_,x[4],y[4];
ll cross(ll a, ll b, ll c, ll d) {
    return (a*d-b*c)>0?1:-1;
}
bool in(ll a, ll b, ll c) {
    // a是否在bc范围内
    if(b>c) swap(b,c);
    return a>=b && a<=c;
}

bool solve() {
    ll ok=0, sign=0;
    for(ll i=1;i<=3;++i){
        ll a=x[i],b=y[i],c=x[i%3+1],d=y[i%3+1];
        // 判断在边上
        if(((d-b)*(x0_-a)==(y0_-b)*(c-a) && in(x0_,a,c) && in(y0_,b,d)) || (x0_==a&&y0_==b)){
            ok=1;
            break;
        }
        // 判断叉乘符号
        sign+=cross(a-x0_,b-y0_,c-x0_,d-y0_);
    }
    if(ok || abs(sign)==3) return true;
    return false;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin>>x[2]>>y[2]>>x[3]>>y[3];

    for(ll i=0;i<=max(x[2],x[3]);++i){
        for(ll j=0;j<=max(y[2],y[3]);++j){
            x0_=i,y0_=j;
            if(solve()){
                cout<<'#';
            }
            else{
                cout<<'.';
            }
        }
        cout<<'\n';
    }

    return 0;
}

Problem M

先令\(x=y\),得到总和\(\sum w_i\),之后询问\(n-2\)条边,得到边权异或总和,这个结果再异或上第一步得到的总和得到边权,从而得到\(n-2\)条边的边权,剩下一条边用总和减掉其他边即可。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

ll n, u[100005], v[100005], ans[100005];

void solve() {
    cin>>n;
    ll w=0;
    for(ll i=1;i<n;++i){
        cin>>u[i]>>v[i];
    }
    cout<<"? "<<1<<" "<<1<<endl;
    cin>>w;
    for(ll i=1;i<n-1;++i){
        cout<<"? "<<u[i]<<" "<<v[i]<<endl;
        cin>>ans[i];
        ans[i]^=w;
    }
    for(ll i=1;i<n-1;++i){
        w-=ans[i];
    }
    ans[n-1]=w;
    string o="! ";
    for(ll i=1;i<n;++i){
        o+=to_string(ans[i]);
        if(i<n-1) o+=" ";
    }
    cout<<o<<endl;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t = 1;
    // cin >> t;
    while (t--) {
        solve();
    }

    return 0;
}

Problem N

\(b_i=f_i-f_{i-1}\)\(c_i=\sum g_i\)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

void solve() {
    ll n,m;
    cin>>n>>m;
    ll f=0,g=0,x;
    for(ll i=1;i<=n;++i){
        cin>>x;
        cout<<x-f<<' ';
        f=x;
    }
    cout<<'\n';
    for(ll i=1;i<=m;++i){
        cin>>x;
        cout<<x+g<<' ';
        g+=x;
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t = 1;
    // cin >> t;
    while (t--) {
        solve();
    }

    return 0;
}

Problem O

定义\(f[k][i][j]\)表示从\([1,i]\)\(k\)个外卖,价格总和模\(t\)\(j\)的方案中,美味值之和最大值。
\(f[k][i][j]=\max\{f[k-1][i-1][(j-a[i]+t)\%t]+b[i],f[k][i-1][j]\}\)\(\max\)的第一项表示选第\(i\)个外卖,第二项表示不选。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

ll n,t;
ll a[505], b[505], f[505][505][55];

void solve() {
    cin>>n>>t;
    for(ll i=1;i<=n;++i) cin>>a[i];
    for(ll i=1;i<=n;++i) cin>>b[i];
    for(ll i=1;i<=n;++i)
        f[i][1][a[i]%t]=max(f[i][1][a[i]%t],b[i]);
    for(ll i=2;i<=n;++i){
        for(ll j=1;j<=n;++j){
            for(ll k=0;k<t;++k){
                f[i][j][k]=max(f[i-1][j][k],f[i][j][k]);
                if(f[i-1][j-1][k])
                    f[i][j][(k+a[i])%t]=max(f[i-1][j-1][k]+b[i],f[i][j][(k+a[i])%t]);
            }
        }
    }
    for(ll k=1;k<=n;++k){
        cout<<(f[n][k][0]==0 ? -1 : f[n][k][0])<<' ';
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t = 1;
    // cin >> t;
    while (t--) {
        solve();
    }

    return 0;
}
posted @ 2025-11-29 21:33  TimeLimit  阅读(27)  评论(0)    收藏  举报