26牛客多校8

   本场感觉题解的题意概括挺好的,故直接引用,略做修改

I

给定两两配对的选手及其初始得分,每场比赛可将m分任意分配给该组选手。求Soy在所有分配方案中可能取得的最好名次与最坏名次。

分别求最优情况和最劣情况,并对每一场比赛独立贪心处理。

考虑最少有多少人超过Soy,

为了让 Soy 尽可能高,直接让他在第一场拿到全部 m 分,因此:

此时逐场考虑其他比赛,对于(ai ,ai+1),先令前者小于后者,此时分类讨论:

  • ai > b : 无论怎么分,两人都超过 Soy,贡献 2。
  • ai+1 > b且 ai <= b : 至少有 1人超过。
  • 两人初始分数都不超过 b:判断剩余的 m - ( b - ai )分能否让另一人也超过 b,若能则贡献 1,否则贡献 0。

第一场 (a1 ,a2)单独判断,因为 Soy 本身参与其中。

对于最多有多少人超过Soy的情况,有类似做法。最终复杂度O(n)



G

构造四个无前导零的十进制正整数,使x1,x2的前a位相同、y1,y2 的前b位相同,而x1y1与x2y2 的前c位不同,并满足长度限制

易知只要让最终的乘积恰好产生进位即可,构造。



B

统计Ushio在部分手牌已被指定时,从1到2n中选择n张牌,使其无论Fuuko如何出牌都能逐轮用不大于对方的牌应对并获胜的方案数。

考虑把 1~2n 中选入 Ushio 手牌的数字记为 1,未选入的数字记为 0。

为了保证无论 Fuuko 如何出牌,Ushio 都能应对,选出的 n 个数必须满足:对任意前缀[1,i],其中选入 Ushio 的数字数量不能少于未选入的数量。

否则,若某个前缀中 Fuuko 的数字更多,Fuuko 可以优先打出这些较小的数字,使 Ushio 被迫使用更大的数字,从而输掉比赛。

因此合法的选法等价于一个长度为 2n 的合法括号序列:

  • 选入 Ushio:看作 ( ;
  • 不选入 Ushio:看作 ) ;
  • 最终两者数量相等。

又因为 2n 一定会被选入或者不选入,所以可以发现只需要枚举 1~2n-1,最后一个位置的状态由前面的选择唯一确定。

设计dp:令 dp[i] 表示处理完当前数字后:

转移:

  • 当前数字选给 Ushio:i->i+1;
  • 当前数字不选给 Ushio:i->i-1。

如果某个数字是强制选择的,就不能执行 i->i-1 的转移。

最后处理完 1~2n 后,必须让最后一个数字 2n 被选入 Ushio,因此要求当前差值为 1,所以答案是:dp[1]

特殊的,由于 Ushio 一共只能选择 n 个数字,而上述括号结构中最大的数字 2n 是特殊的最后一个位置。若强制数字 x>2n-1,说明强制数字不可能处于代码构造的有效范围内,因此直接输出 0。

B.Deep Finesse
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define fep(i,j,k) for(int i=j;i<=k;i++)
#define fepp(i,j,k) for(int i=j;i<k;i++)
#define fepd(i,j,k,l) for(int i=j;i<=k;i+=l)
#define feppd(i,j,k,l) for(int i=j;i<k;i+=l)
#define fed(i,j,k) for(int i=j;i>=k;i--)
#define fedd(i,j,k) for(int i=j;i>k;i--)
#define fedp(i,j,k,l) for(int i=j;i>=k;i-=l)
#define feddp(i,j,k,l) for(int i=j;i>k;i-=l)
using namespace std;
const int N=1e5+5;
const int mod=998244353;
void solve()
{
	int n,m,x;
	cin>>n>>m;
	vector<int> a(2*n+1);
	bool F=true;
	fep(i,1,m)
	{
		cin>>x;
		if(x>2*n-1) F=false;
		else a[x]=1;
	}
	if(!F)
	{
		cout<<0<<endl;
		return;
	}
	vector<int> dp(n+1),ndp(n+1);
	dp[0]=1;
	fep(i,1,2*n-1)
	{
		fill(ndp.begin(),ndp.end(),0);
		fep(j,0,n)
		{
			if(!dp[j]) continue;
			if(j<n)ndp[j+1]=(ndp[j+1]+dp[j])%mod;
			if(!a[i]&&j>0)ndp[j-1]=(ndp[j-1]+dp[j])%mod;
		}
		dp.swap(ndp);
	}
	cout<<dp[1]<<endl;
	return;
}
signed main()
{
	IOS
	int t;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}


M

给定一系列字符串请求与容量为m的Trie前缀缓存。处理请求会计算并缓存路径上的结点,超容量时只能删除叶结点;在已知全部未来请求时,求最小总计算代价。

队友赛时T了两发,据说是操作系统课的调页算法的Bélády OPT 算法。

核心思想是 把每条 Trie 边看成一个缓存块,用 Belady 最优缓存策略——每次删除“下一次最晚才会用到”的叶子边,比较神秘,等学了操作系统再来看看()



H

给定非负整数数组。每次先任选一个元素加1,再选择一个不小于x的元素减去x;求经过若干次合法操作后能够达到的最小数组元素和。

对于每个 a[i],按 x 拆成:

其中:

  •  可以看成拥有的完整 x;
  • 是剩余部分。

把所有完整的 x 加起来:

然后只保留所有余数 ri,排序。

贪心

如果想把某个余数 r 补成 x-1,还需要:个完整单位。

因此从最大的余数开始处理,因为余数越大,需要消耗的 sum 越少,优先补它最划算。

若:就用 sum 补满它,并令该余数变成 0:

处理完所有余数后,剩余的 sum 只能按照 x-1 的周期贡献,因此最后:

再加入答案。

所以整体就是:

将每个数拆成  和 ,把所有完整块汇总成 sum,然后优先用 sum 补最大的余数到 x-1,最后剩余的完整块按 x-1 取模。

特殊的,x=1 时 ,上述过程无法使用,因此直接把所有 a[i] 求和取模。

H. It’s a Magic, Not a Trick!
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define lr(i, a, b) for(int i = a; i <= b; i++)
#define rl(i,b,a) for(int i=b;i>=a;i--)
const int maxn=1e6+10;
const int inf=4e18+10;
const int mod=998244353;
int a[maxn];
int b[maxn];
void solve()
{
    int n,x;
    cin>>n>>x;
    int ans=0;
    if(x==1)
    {
        lr(i,1,n)
        {
            cin>>a[i];
            ans=(ans+a[i]%mod)%mod;
        }
        cout<<ans<<endl;
    }
    else
    {
        __int128 sum=0;
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            b[i]=a[i]%x;
            sum+=(__int128)(a[i]/x);
        }
        sort(b+1,b+1+n);
        // cout<<(int)sum<<endl;
        for(int i=n;i>=1;i--)
        {
            if(sum>=(x-1-b[i]))
            {
                sum=sum-(x-1-b[i]);
                // ans=(ans+1)%mod;
                b[i]=0;
            }
            ans=(ans+b[i])%mod;
        }
        ans=(ans+(sum%(x-1))%mod)%mod;
        cout<<ans<<endl;
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);

    int T = 1;
    cin >> T;
    while(T--)
        solve();

    return 0;
}


C

给定质数P与上界n。对每个1 ≤ a < b ≤ n,令x = a * b−1 mod P;统计满足 a / b > x / P 的数对(a,b)。

先把原问题化成 gcd 的求和,再利用欧拉函数的约数恒等式把它转成对 P+1 所有因数的枚举;由于 P+1 很大,因此用 Pollard-Rho + Miller-Rabin 完成质因数分解。

C.Fraction on a Ring
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
#define i128 __int128
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define fep(i,j,k) for(int i=j;i<=k;i++)
#define fepp(i,j,k) for(int i=j;i<k;i++)
#define fepd(i,j,k,l) for(int i=j;i<=k;i+=l)
#define feppd(i,j,k,l) for(int i=j;i<k;i+=l)
#define fed(i,j,k) for(int i=j;i>=k;i--)
#define fedd(i,j,k) for(int i=j;i>k;i--)
#define fedp(i,j,k,l) for(int i=j;i>=k;i-=l)
#define feddp(i,j,k,l) for(int i=j;i>k;i-=l)
using namespace std;
const int mod=998244353;
int qpow(int a,int b,int p)
{
    int r=1;
    while(b)
    {
        if(b&1) r=(i128)r*a%p;
        a=(i128)a*a%p;
        b>>=1;
    }
    return r;
}
int gcd(int a,int b)
{
    while(b)
    {
        int t=a%b;
        a=b;
        b=t;
    }
    return a;
}
bool isprime(int n)
{
    if(n<2) return 0;
    for(int p:{2,3,5,7,11,13,17,19,23,29,31,37})
    {
        if(n%p==0) return n==p;
    }
    int d=n-1,s=0;
    while(!(d&1)) d>>=1,s++;
    for(int a:{2,325,9375,28178,450775,9780504,1795265022})
    {
        if(a%n==0) continue;
        int x=qpow(a%n,d,n);
        if(x==1||x==n-1) continue;
        bool ok=0;
        fep(i,1,s-1)
        {
            x=(i128)x*x%n;
            if(x==n-1)
            {
                ok=1;
                break;
            }
        }
        if(!ok) return 0;
    }
    return 1;
}
int rho(int n)
{
    if(n%2==0) return 2;
    if(n%3==0) return 3;
    for(int c=1;;c++)
    {
        int x=2,y=2,d=1;
        while(d==1)
        {
            x=((i128)x*x+c)%n;
            y=((i128)y*y+c)%n;
            y=((i128)y*y+c)%n;
            d=gcd(abs(x-y),n);
        }
        if(d!=n) return d;
    }
}
vector<int> v;
vector<pair<int,int>> f;
int n,ans;
void fact(int x)
{
    if(x==1) return;
    if(isprime(x))
    {
        v.push_back(x);
        return;
    }
    int d=rho(x);
    fact(d);
    fact(x/d);
}
void dfs(int k,int d,int ph)
{
    if(d>n) return;
    if(k==(int)f.size())
    {
        ans=(ans+(i128)(ph%mod)*(n/d))%mod;
        return;
    }
    dfs(k+1,d,ph);
    int pw=1;
    int pp=f[k].first-1;
    fep(i,1,f[k].second)
    {
        if(d>n/f[k].first) break;
        d*=f[k].first;
        if(i==1) pw=1;
        else pw*=f[k].first;
        dfs(k+1,d,(i128)ph*pp%mod);
        pp=(i128)pp*f[k].first%mod;
    }
}
void solve()
{
    cin>>n;
    int P;
    cin>>P;
    v.clear();
    f.clear();
    fact(P+1);
    sort(v.begin(),v.end());
    for(int x:v)
    {
        if(f.empty()||f.back().first!=x)f.push_back({x,1});
        else f.back().second++;
    }
    ans=0;
    dfs(0,1,1);
    int s=(i128)(n%mod)*((n+1)%mod)%mod;
    s=s*((mod+1)/2)%mod;
    s=(s-ans+mod)%mod;
    s=s*((mod+1)/2)%mod;
    cout<<s<<endl;
}
signed main()
{
    IOS
    int t;
    cin>>t;
    while(t--) solve();
    return 0;
}


posted @ 2026-08-17 14:34  lemon_233  阅读(14)  评论(0)    收藏  举报