2024 National Invitational of CCPC (Zhengzhou), 2024 CCPC Henan Provincial Collegiate Programming Contest 2024 ccpc 全国邀请赛(郑州) 10/13

25郑州邀请赛报名前一天vp了一下,7题659罚时,下班太晚了不想接着写了,剩下的时间应该还能开一题。

第二天10点报名,开始报名后11秒名额就抢光了,网卡了一下,报名顺序很靠后,直接被reject。

下午六点左右,递补名额ac

补题10/13

Problem F. 优秀字符串

签到,直接模拟即可

点击查看代码
#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;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){   
    int n,ans=0;
    cin>>n;
    while(n--){
        string s;
        cin>>s;

        if(s.size()!=5) continue;
        if(s[2]!=s[4]) continue;
        set<char> st;
        for(int i=0;i<4;i++){
            st.insert(s[i]);
        } 
        if(st.size()==4) ans++;
    }
    cout<<ans;
}   

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

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

    return 0; 
}

B. 扫雷 1

一眼反悔贪心,但wa了三发,读错题wa一发,细节wa两发

从前往后能买就买,同时把买单个价格和购买个数放进大根堆里。

当在后面遇到一个价格比较小的,则退掉前面已经买了且单价比当前这个大的,把钱全买这个

点击查看代码
#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;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 998244353;

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

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

    int now=0;
    priority_queue<pii> q;

    for(int i=1;i<=n;i++){
        now++;
        while(q.size()){
            auto [val,cnt]=q.top();
            if(val>a[i]){
                now+=val*cnt;
                q.pop();

                q.push({a[i],now/a[i]});
                now%=a[i];
                
            }
            else break;
        }
        if(now>=a[i]){
            q.push({a[i],now/a[i]});
            now%=a[i];
        }
    }

    int ans=0;
    while(q.size()){
        ans+=q.top().second;
        q.pop();
    }
    cout<<ans;
}   

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

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

    return 0; 
}

Problem M. 有效算法

一眼二分,显然当k在大于一个值后就一定满足条件了。

对每一个a[i],可以找到x的一个区间,如果所有区间有并集,则返回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;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){   
    int n;
    cin>>n;
    vector<int> a(n+10),b(n+10);

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

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

    auto check=[&](int k)->bool {
        int l=-inf,r=inf;
        for(int i=1;i<=n;i++){
            int tl=a[i]-k*b[i], tr=a[i]+k*b[i];
            if(i==1){
                l=tl;
                r=tr;
            }
            else{
                if(tl<l){
                    if(tr<l) return 0;
                    else r=min(r,tr);
                }else{
                    //tl >= l
                    if(tl>r) return 0;
                    else{
                        l=tl;
                        r=min(tr,r);
                    }
                }
            }
            // if(k==1) cout<<a[i]<<"  "<<l<<" "<<r<<endl;
        }
        return 1;
    };


    int l=0,r=1e9;

    while(l<r){
        int mid=l+r>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }

    cout<<l<<endl;
}   

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

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

    return 0; 
}

J. 排列与合数

纯纯诈骗题,当有偶数的时候,直接把偶数诺到第一位即可。

否则直接输出样例里给出的那个就行。

注意特判前导零

点击查看代码
#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;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){   
    string s;
    cin>>s;

    for(int i=0;i<5;i++){
        if(s[i]=='0'){
            swap(s[i],s[4]);
            cout<<s<<endl;
            return;
        }
    }

    for(int i=0;i<5;i++){
        int val=s[i]-'0';
        if(val%2==0){
            swap(s[i],s[4]);
            cout<<s<<endl;
            return;
        }
    }

    cout<<"97531"<<endl;
}   

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

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

    return 0; 
}

L. Toxel 与 PCPC II

一开始写了个贪心,wa了一发发现好像不太能贪

dp,因为四次方的数值增长很大,所以dp[i]一定不会从离的很远地方转移过来,可以设成70

点击查看代码
#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;
//using i128 = __int128;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){   
    int n,m;
    cin>>n>>m;
    vector<int> a(m+10),f(m+10,inf);
    for(int i=1;i<=m;i++){
        cin>>a[i];
    }

    f[0]=0;

    for(int i=1;i<=m;i++){
        int l=max(0ll,i-70);
        for(int j=l;j<i;j++){
            int val=f[j]+pow(i-j,4)+a[i];
            f[i]=min(f[i],val);
        }
    }

    cout<<f[m]<<endl;

    
}   

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

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

    return 0; 
}

H. 随机栈

每次取数时,只能取集合里的最小值。

所以每次取数时,给答案成上取出最小值的概率即可。

在放数时,如果放的数比之前已经取出的数要小,则直接输出0.

点击查看代码
#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;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 998244353;

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

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

    int ans=1;
    int sum=0;
    int now=-inf;
    map<int,int> mp;
    priority_queue<int,vector<int>,greater<int>> q;

    for(int i=1;i<=2*n;i++){
        int ch;
        cin>>ch;
        if(ch==-1){
            int val=q.top();
            q.pop();

            now=val;
            //ans*=mp[val]/sum
            // cout<<mp[val]<<" "<<sum<<endl;
            ans*=mp[val]*qmi(sum,mod-2,mod)%mod;
            ans%=mod;
            mp[val]--;
            sum--;
        }
        else{
            if(ch<now){
                cout<<0<<endl;
                return;
            }
            sum++;
            q.push(ch);
            mp[ch]++;
        }
    }
    cout<<ans<<endl;
}   

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

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

    return 0; 
}

K. 树上问题

换根dp很板的题,每次把根从u换到子节点v时,只有u和v的值会发生变化,在dfs时维护u和v的值即可

点击查看代码
#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;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 998244353;

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

    vector<int> w(n+10);
    vector<vector<int>> g(n+10);

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

    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }

    int ans=0;
    int sum=0; 
    vector<int> f(n+10);//每个节点是否满足条件

    auto dfs1=[&](auto dfs1,int u,int pre)->void {
        if(w[u]>=(w[pre]+1)/2){
            f[u]=1;
            sum++;
        }
        for(auto v:g[u]){
            if(v==pre) continue;
            dfs1(dfs1,v,u);
        }
    };
    

    dfs1(dfs1,1,0);

    if(sum==n) ans++;

    // cout<<f[5]<<" "<<sum<<endl;

    auto dfs=[&](auto dfs,int u,int pre)->void {
        for(auto v:g[u]){
            if(v==pre) continue;

            int tv=f[v],tu=f[u];
            if(u==1 && v==5){
                // cout<<f[5]<<" "<<sum<<endl;
            }
            if(f[v]==0){
                f[v]=1;
                sum++;
            }
            if(w[u]>=(w[v]+1)/2){
                if(f[u]==0){
                    f[u]++;
                    sum++;
                }
            }
            else{
                if(f[u]==1){
                    f[u]--;
                    sum--;
                }
            }

            if(sum==n) ans++;
            dfs(dfs,v,u);

            sum+=tv-f[v];
            sum+=tu-f[u];
            f[v]=tv;
            f[u]=tu;
            
            if(u==1 && v==5){
                // cout<<f[v]<<" "<<sum<<endl;
            }
        }
    };

    dfs(dfs,1,0);

    cout<<ans<<endl;
}   

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

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

    return 0; 
}

Problem A. Once In My Life

vp时看了会没啥想法的题,24的学弟学妹说这题不是简单题吗?

假设构造的数是 t = 123456789d0000,最后0的个数是n的位数

假设t不是n的倍数,要给t加上一些数,满足t是n的倍数

具体的,要给 t += (n-t%n)

最后t/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;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int n,d;
    cin>>n>>d;
    int tmp=n;

    int t=1234567890+d;

    int cnt=0;
    while(tmp){
        tmp/=10;
        cnt++;
    }

    t*=pow(10,cnt);

    t+=(n-t%n);
    cout<<t/n<<endl;
    // cout<<t%n<<endl;
}   

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

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

    return 0; 
}

Problem C. 中二病也要打比赛

首先假设有两个相同的数a[i] = a[j],则 i ~ j 这一段最后都要推平,就是让这一段的值都相等,就是在这段区间中,选一个数x,f(x)=x,其他的值f(y)=x (y!=x)。所以这一段区间中,一共有sum种数,则代价一定是sum-1

如果两端区间相交怎么办?

例如 1,2,3,2,3,5

则一定要让a[2]到a[5]都一样。

换句话说,如果有两个区间要推平,且两个区间相交,则这两个区间必须要推平一个数

考虑全局的最长非严格上升子序列,则每个区间内选且仅选一种数字。

为了让每段区间之选一种数字,可以给每段要推平的区间,从大到小排序,再对整个数组计算lis

处理的时候有三个技巧

  1. 如何找到每段要从大到小排序的段?因为有出现区间会相交的可能,我们可以找到每个数字,第一次出现的地方fir,和最后一次出现的地方lst,对这段区间差分,给d[fir]+1, d[lst]-1。最后前缀和后,等于0的地方就是一段区间的终点

  2. 因为要统计每段种有多少种数,的f(x)!=x, 可以计算整个数组的严格lis(严格lis中不会出现相同的数),此时在每个段中,只会选一个数,统计整个数组有多少个数,减去严格lis的长度,即为答案

  3. 因为n是1e5,所以要二分的找lis。

点击查看代码
#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;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 998244353;

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

    int cnt=0;
    vector<int> a(n+10),fir(n+10),lst(n+10),d(n+10);

    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(fir[a[i]]==0){
            fir[a[i]]=i;
            cnt++;
        }
        lst[a[i]]=i;
    }

    for(int i=1;i<=n;i++){
        d[fir[i]]++;
        d[lst[i]]--;
    }

    for(int i=1;i<=n;i++){
        d[i]+=d[i-1];
    }

    int pre=1;
    for(int i=1;i<=n;i++){
        if(d[i]==0){
            sort(a.begin()+pre,a.begin()+i+1,greater<int>());
            pre=i+1;
        }
    }

    int len=1;
    d.assign(n+10,0);
    d[1]=a[1];

    for(int i=2;i<=n;i++){
        if(a[i]>d[len]){
            d[++len]=a[i];
        }
        else{
            int l=1,r=len;
            while(l<r){
                int mid=l+r>>1;
                if(d[mid]>=a[i]) r=mid;
                else l=mid+1;
            }
            d[l]=a[i];
        }
    }
    cout<<cnt-len<<endl;
}


signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

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

    return 0; 
}

Problem D. 距离之比

显然答案最小是1,最大是在两点的连线是45°或135°时,取到最大值根号2

且这个取值只跟两点连线的角度有关系。

可以把坐标轴逆时针旋转45°,点坐标转换成 ( x+y ,x-y )

此时的y轴也就是135°,x轴就是45°

先对所有点,按照x+y排序,此时两个相邻点a,a+1在x-y上的差距一定小于不相邻的点a,a+2

x-y同理

所以两次排序分别对相邻点计算答案取最大值即可

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

void solve(){   
    int n;
    cin>>n;
    vector<pdd> a(n+1);
    for(int i=1;i<=n;i++){
        cin>>a[i].first>>a[i].second;
        // scanf("%lf %lf",&a[i].first,&a[i].second);
    }

    sort(a.begin()+1,a.end(),[&](pdd x,pdd y)-> bool {
        return x.first+x.second<y.first+y.second;
    });

    double ans=1.0;

    auto getdist = [&](pdd t1,pdd t2)->double {
        auto [x1,y1]=t1;
        auto [x2,y2]=t2;

        double dx=abs(x1-x2);
        double dy=abs(y1-y2);
        return (dx+dy)/sqrt((dx*dx+dy*dy));
    };

    for(int i=1;i<n;i++){
        auto [x1,y1]=a[i];
        auto [x2,y2]=a[i+1];

        double val=getdist(a[i],a[i+1]);
        ans=max(ans,val);
    }

    sort(a.begin()+1,a.end(),[&](pdd x,pdd y)-> bool {
        return x.first-x.second<y.first-y.second;
    });

    for(int i=1;i<n;i++){
        auto [x1,y1]=a[i];
        auto [x2,y2]=a[i+1];

        double val=getdist(a[i],a[i+1]);
        ans=max(ans,val);
    }
    printf("%.11f\n",ans);

}


signed main(){
    // ios::sync_with_stdio(0);
    // cin.tie(0);

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

    return 0; 
}
posted @ 2025-05-14 21:47  LYET  阅读(99)  评论(0)    收藏  举报