Educational Codeforces Round 186 (Rated for Div. 2) A-E

A. New Year String

先判断有没有 2026 和 2025,如果有 2026 或没有 2025,则符合题意,直接输出 0 即可。

否则,此时字符串中一定有 2025,只需要一次修改把 5 改为 6 即可,答案为 1。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int n;
    cin>>n;

    string s;
    cin>>s;

    
    int f1=s.find("2026");
    int f2=s.find("2025");

    if(f1!=string::npos || f2==string::npos){
        cout<<0<<endl;
    }  
    else cout<<1<<endl;

}
	
signed main(){

	ios::sync_with_stdio(0);
	cin.tie(0);

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

}

B. New Year Cake

分两种情况,第一层是白色和第一层是黑色。

直接模拟这两种情况,取最大值即可

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int a,b;
    cin>>a>>b;

    int c=a,d=b;

    int ans1=0,now=1,pre=0;
    while(a || b){
        if(pre==0 && a>=now){
            a-=now;
        }
        else if(pre==1 && b>=now){
            b-=now;
        }
        else break;

        ans1++;
        pre^=1;
        now*=2;
    }

    a=c,b=d,now=1,pre=1;
    int ans2=0;

    while(a || b){
        if(pre==0 && a>=now){
            a-=now;
        }
        else if(pre==1 && b>=now){
            b-=now;
        }
        else break;

        ans2++;
        pre^=1;
        now*=2;
    }

    cout<<max(ans1,ans2)<<endl;
}
	
signed main(){

	ios::sync_with_stdio(0);
	cin.tie(0);

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

}

C. Production of Snowmen

思路分析:核心在于条件的解耦。

稳定性条件 \(a_{i+m} < b_{j+m} < c_{k+m}\) 可以拆分为两个独立的部分:

\(a\)\(b\) 的关系:对于所有 \(m\)\(a_{i+m} < b_{j+m}\) 必须成立,这只取决于 \(i\)\(j\) 的相对偏移量 \(d_{ab} = (j - i) \pmod n\)

\(b\)\(c\) 的关系:对于所有 \(m\)\(b_{j+m} < c_{k+m}\) 必须成立。这只取决于 \(j\)\(k\) 的相对偏移量 \(d_{bc} = (k - j) \pmod n\)

算法步骤:

计算合法偏移量:令 valid_ab[d] 表示偏移量为 \(d\) 时,是否满足所有的 \(a < b\)

通过 \(O(n^2)\) 遍历所有的 \(b_x\)\(a_y\),记录哪些偏移量 \((x-y+n) \pmod n\) 满足 \(b_x > a_y\)。只有当某个偏移量 \(d\) 被满足了 \(n\) 次(即对于每一个 \(b\) 的位置都成立)时,该偏移量才合法。

同理计算 \(b\)\(c\) 的合法偏移量数量 cnt_bc。

统计答案:如果合法的 \(a \to b\) 偏移量有 \(cnt1\) 个,合法的 \(b \to c\) 偏移量有 \(cnt2\) 个。

对于每一对合法的相对偏移量 \((d_{ab}, d_{bc})\),中间的起始位置 \(j\) 可以有 \(n\) 种选择(\(1 \le j \le n\)),一旦 \(j\) 确定,\(i\)\(k\) 也就唯一确定了。

最终答案为:\(cnt1 \times cnt2 \times n\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int n;
    cin>>n;

    vector<int> a(n),b(n),c(n);

    for(int i=0;i<n;i++){
        cin>>a[i];
    }
    for(int i=0;i<n;i++){
        cin>>b[i];
    }
    for(int i=0;i<n;i++){
        cin>>c[i];
    }

    vector<int> preb(n),sufb(n);
    
    for(int i=0;i<n;i++){
        vector<int> tmp(n);
        for(int j=0;j<n;j++){
            if(b[i]>a[j]) tmp[(i-j+n)%n]=1;
        }
        if(i==0) preb=tmp;
        else{
            for(int i=0;i<n;i++){
                preb[i]*=tmp[i];
            }
        }
    }

    int ans=0;
    for(int i=0;i<n;i++){
        ans+=preb[i];
    }
    ans*=n;

    // cout<<ans<<endl;

    for(int i=0;i<n;i++){
        vector<int> tmp(n);
        for(int j=0;j<n;j++){
            if(b[i]<c[j]) tmp[(i-j+n)%n]=1;
        }
        if(i==0) sufb=tmp;
        else{
            for(int i=0;i<n;i++){
                sufb[i]*=tmp[i];
            }
        }
    }

    int sum=0;
    for(int i=0;i<n;i++){
        sum+=sufb[i];
    }

    ans*=sum;

    cout<<ans<<endl;

}
	
signed main(){

	ios::sync_with_stdio(0);
	cin.tie(0);

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

}

D. Christmas Tree Decoration

组合数学

任务分配:每个人到底要挂多少个?

首先我们要搞清楚,挂装饰品的过程是循环的。

假设总装饰品数量是 \(S = \sum_{i=0}^n a_i\)。一共有 \(n\) 个人,每轮过一遍。通过简单的带余除法:\(k = S / n\)\(r = S \% n\)

这意味着:在排列 \(p\) 中,前 \(r\) 个人每个人要挂 \(k+1\) 个,后 \(n-r\) 个人每个人要挂 \(k\) 个。

非法情况分析:什么时候绝对不行?

每个人 \(i\) 有自己的盒子 \(a_i\),如果他不够用,就必须去 0 号盒子拿。

最轻松的情况:如果一个人只需要挂 \(k\) 个,但他自己的盒子 \(a_i < k\),那么他必须从 0 号盒子里拿 \(k - a_i\) 个。

底线:即使我们把所有人放在最轻松的位置上(只挂 \(k\) 个),如果所有人对 0 号盒子的“刚需”加起来超过了 \(a_0\),那么任何排列都不可能成功。

即:如果 \(\sum \max(0, k - a_i) > a_0\),直接输出 0。

最优排列分析:谁该排在前面?

现在我们要分配那 \(r\) 个“重担”(挂 \(k+1\) 个的名额)。

如果你盒子里装饰品很多(\(a_i\) 很大),你多挂一个可能也不需要动用 0 号盒子。如果你盒子里装饰品很少(\(a_i\) 很小),你多挂一个就必须多从 0 号盒子拿一个。

结论:为了让 0 号盒子撑得久一点,盒子最满的人应该排在前面。

如果你按 \(a_i\) 从大到小排序后的排列都不能满足条件,那别的排列更不行。

一般情况分析:利用组合数学计数理解了“谁排前面更好”,我们就可以进行分类计数了:

第一类人(不敏感型):\(a_i > k\)。这些人即使被分到前 \(r\) 个位置(挂 \(k+1\) 个),对 0 号盒子的需求也不会比在后面多(或者增加得很少,特别是当 \(a_i \ge k+1\) 时,增加量为 0)。

第二类人(敏感型):\(a_i \le k\)。这些人如果在前 \(r\) 个位置,他们必须比在后面多从 0 号盒子拿走 正好 1 个 装饰品。

计数逻辑:计算 0 号盒子的“剩余战斗力” \(K\)\(K = a_0 - (\text{所有人都在后面时的总刚需})\)

统计“敏感型”人数 \(cnt\)。我们需要在 \(r\) 个“重担位”中选出一些位置。

假设我们选了 \(i\) 个“敏感型”人放在前 \(r\) 位,那么他们会额外消耗 0 号盒子 \(i\) 个。

所以必须满足 \(i \le K\)

公式:对于每一个合法的 \(i\)\(0 \le i \le \min(cnt, r, K)\)):

\(cnt\) 个敏感型人中选 \(i\) 个放在前 \(r\) 位:

\(C(cnt, i)\)\(n - cnt\) 个不敏感型人中选 \(r - i\) 个填满前 \(r\) 位:\(C(n - cnt, r - i)\)\(r\) 位内部可以任意排:\(r!\)

\(n-r\) 位内部可以任意排:\((n-r)!\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

const int N=1e6+10;
vector<int> fact(N),infact(N);

int qmi(int a,int b,int p){
    int res=1;
    while(b){
        if(b&1) res=res*a%p;
        b>>=1;
        a=a*a%p;
    }
    return res;
}

void init(){
    fact[0]=1;
    for(int i=1;i<=1e6;i++){
        fact[i]=fact[i-1]*i%mod;
    }

    infact[1e6]=qmi(fact[1e6],mod-2,mod);
    for(int i=1e6-1;i>=1;i--){
        infact[i]=infact[i+1]*(i+1)%mod;
    }
    infact[0]=1;
}

int C(int a,int b){
    return fact[a]*infact[b]%mod*infact[a-b]%mod;
}

void solve(){
    int n;
    cin>>n;

    vector<int> a(n+1);
    
    for(int i=0;i<=n;i++){
        cin>>a[i];
    }

    int mx=*max_element(a.begin()+1,a.end());
    int mxcnt=0,nd=0;

    for(int i=1;i<=n;i++){
        if(a[i]==mx) mxcnt++;
        else nd+=(mx-1-a[i]);
    }

    if(a[0]<nd){
        cout<<0<<endl;
        return;
    }

    a[0]-=nd;
    int pos=min(n,mxcnt+a[0]);

    //前面有 pos 个位置,要填进去 mxcnt 个数
    //还需要从后面的数中,选pos-mxcnt 个进来

    //C(n-mxcnt,pos-mxcnt) * fact[pos] * fact[n-pos]
    int ans=C(n-mxcnt,pos-mxcnt)*fact[pos]%mod*fact[n-pos]%mod;
    cout<<ans%mod<<endl;
}
	
signed main(){

	ios::sync_with_stdio(0);
	cin.tie(0);

    init();

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

}

E. New Year's Gifts

成本转换每个朋友基础必须花费 \(y_i\),这是不可避免的支出。

我们将总预算 \(k\) 预先减去所有 \(y_i\),剩下的 \(k' = k - \sum y_i\) 就是我们用来“购买快乐”的额外预算。

对于每一个朋友,让他开心的额外代价有两种:

方案 A(使用盒子): 消耗一个美丽度 \(a_j \ge x_i\) 的盒子,额外金钱代价为 \(0\)

方案 B(提升价值): 不使用盒子,额外金钱代价为 \(c_i = z_i - y_i\)

核心贪心策略由于我们的目标是最大化人数,这是一个典型的资源分配问题。

我们手里有两种资源:有限的盒子和有限的金钱。

策略一:如何分配盒子?

盒子相当于一张“面值为 \(c_i\) 的代金券”。

为了让盒子的价值最大化,我们应该优先把盒子分给那些“如果不给盒子,变开心就会很贵”的朋友。

因此,我们将朋友按 \(c_i\)(即 \(z_i - y_i\))从大到小排序。

策略二:如何选择具体的盒子?

对于当前的朋友,如果有多款盒子满足 \(a_j \ge x_i\),我们应该选哪一个?

为了给后面的朋友留下更多可能性,我们应该选择满足条件的美丽度最小的盒子。这可以通过 multiset 的 lower_bound 实现。

策略三:如何使用剩余的金钱?

当所有的盒子都分配完,或者剩下的朋友无法匹配到盒子时,我们手里还剩下一些钱。

此时,我们只需将剩下这些朋友的 \(c_i\) 从小到大排序,用剩余的预算尽可能多地购买快乐。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

struct node{
    int x,y,z;
    bool operator<(node &t){
        return z-y>t.z-t.y;
    }
};

void solve(){
    int n,m,k;
    cin>>n>>m>>k;

    vector<int> a(m+1);
    multiset<int> st;

    for(int i=1;i<=m;i++){
        cin>>a[i];
        st.insert(a[i]);
    }

    vector<node> p(n+1);
    for(int i=1;i<=n;i++){
        cin>>p[i].x>>p[i].y>>p[i].z;
        k-=p[i].y;
    }

    sort(p.begin()+1,p.end());

    vector<int> cost;
    int ans=0;

    for(int i=1;i<=n;i++){
        auto it=st.lower_bound(p[i].x);
        if(it!=st.end()){
            ans++;
            st.erase(it);
        }
        else{
            cost.push_back(p[i].z-p[i].y);
        }
    }

    sort(cost.begin(),cost.end());
    for(auto val:cost){
        if(k>=val){
            ans++;
            k-=val;
        }
        else break;
    }

    cout<<ans<<endl;
}
	
signed main(){

	ios::sync_with_stdio(0);
	cin.tie(0);

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

}
posted @ 2025-12-30 00:36  LYET  阅读(86)  评论(0)    收藏  举报