2024CCPC哈尔滨部分题解

按自己做题顺序和题目难易来编写此题解

C

本题是签到题,看了应该都有思路。

思路:我的思路比较简单,遍历一下然后,并且不会有前后相反方向,所以记录上一个方向pre,到下个的那个方向讨论一下另外两个方向,在改一下pre就可以了,代码如下:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define vi vector<int>
#define vvi vector<vi>
#define pb push_back
#define mp make_pair
#define endl '\n'
#define fo(i,l,r) for(int i=(l);i<=(r);++i)
const int N = 1e5+10;
const int mod = 1e9+7;
const int INF = 1e18;
void solve()
{
    int n, m, k;
    cin >> n;
    queue<pair<char, int>> q;
    char s;
    int x;
    for (int i = 0; i < n; ++i){
        cin >> s >> x;
        q.push({s, x});
    }
    int falg = 0;
    cout << 2*n-1 << ' ';
    char pre='#';
    while (!q.empty()){
        pair<char, int> p = q.front();
        if (pre == '#'){
            cout << p.first << endl;
            pre = p.first;
            cout << 'Z' << ' ' << p.second << endl;
            q.pop();
            continue;
        }
        if (pre=='S'){
            if (p.first=='E') cout << 'L' << endl, pre = 'E';
            else cout << 'R' << endl, pre = 'W';
        }
        else if (pre=='N'){
            if (p.first == 'E') cout << 'R' << endl, pre = 'E';
            else cout << 'L' << endl, pre = 'W';
        }
        else if (pre=='E'){
            if (p.first == 'N') cout << 'L' << endl, pre = 'N';
            else cout << 'R' << endl, pre = 'S';
        }
        else if (pre=='W'){
            if (p.first == 'S') cout << 'L' << endl, pre = 'S';
            else cout << 'R' << endl, pre = 'N';
        }
        cout << 'Z' << ' ' << p.second << endl;
        q.pop();
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }
    
    return 0;
}

M

思路:题意很简单,但纯照题写肯定会超时,所以不行,接下来我们就得优化。打表或者自己写一个简单样例会发现规律,和它的因数有关,加数方式如下:

f(4) 因数: 1 2 4 

f(4) : 4 2 2 1 

f(8) 因数: 1 2 4 8 

f(8) : 8 4 4 2 2 2 2 1 

 加数归纳为:因数间隙*(n/该因数)

代码如下:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define vi vector<int>
#define pb push_back
#define endl '\n'
#define fo(i, l, r) for (int i = (l); i <= (r); ++i)
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int INF = 1e18;
void solve()
{
    int n, m, k;
    cin >> n;
    auto geta = [&](int num) -> vi
    {
        vi fac;
        for (int i = 1; i * i <= num; ++i)
        {
            if (num % i == 0)
            {
                fac.pb(i);
                if (i != num / i)
                {
                    fac.pb(num / i);
                }
            }
        }
        sort(fac.begin(), fac.end());
        return fac;
    };
    vi a = geta(n);
    int pre = 1;
    int res = 0;
    for (auto x : a){
        res += (x - pre) * (n / pre);
        pre = x;
    }
    cout << res + 1 << endl;
}

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

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

    return 0;
}

G

思路:跑一个bfs,记录要输出的结果就行了

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define vi vector<int>
#define vvi vector<vi>
#define pb push_back
#define mp make_pair
#define endl '\n'
#define fo(i,l,r) for(int i=(l);i<=(r);++i)
const int mod = 1e9+7;
const int INF = 1e18;
const int N=2e5+5;
int n,m,k,x,y;
bool b[N],vis[N];
vector<int> e[N];
void adde(int x,int y){
    e[x].push_back(y);
}
signed main(){
    cin>>n>>m>>k;
    for(int i=1;i<=k;++i)cin>>x,b[x]=1;
    for(int i=1;i<=m;++i)cin>>x>>y,adde(x,y),adde(y,x);
    queue<int> q;
    for(int i=1;i<=n;++i)if(!b[i]){q.push(i),vis[i]=1;break;}
    vector<vector<int>> ans;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        if(b[u])continue;
        vector<int> c;
        c.push_back(u);
        for(int v:e[u])if(!vis[v])c.push_back(v),q.push(v),vis[v]=1;
        if(c.size()>1)ans.push_back(c);
    }
    int cnt=0;
    for(int i=1;i<=n;++i)if(!vis[i])++cnt;
    if(cnt){cout<<"No\n";return 0;}
    cout<<"Yes\n";
    cout<<ans.size()<<'\n';
    for(auto c:ans){
        cout<<c[0]<<' '<<c.size()-1<<' ';
        for(int i=1;i<c.size();++i)cout<<c[i]<<' ';
        cout<<'\n';
    }
}

K

思路:保证每个都有最小的,再遍历每个算删除该中算最大,应该从比它大的那里加,又得保证不超出它们得最大,此处可以算个后缀和,再用二分来计算结果,二分之后要判断是否到打了该数,再讨论一下。代码如下:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define vi vector<int>
#define vvi vector<vi>
#define pb push_back
#define mp make_pair
#define endl '\n'
#define dbug() cout << '||||||debug|||||' << endl;
#define fo(i,l,r) for(int i=(l);i<=(r);++i)
const int N = 1e5+10;
const int mod = 1e9+7;
const int INF = 1e18;
struct node{
    int w, l, r;
};
struct SB{
    int ti, tal;
};
void solve()
{
    int n, m, k;
    int ma= 0;
    cin >> n >> m;
    vector<node> v(n+1);
    for (int i = 1; i<=n; i++){
        cin >> v[i].w >> v[i].l >> v[i].r;
    }    
    vi pre(n+1), ti(n+1);
    sort(v.begin()+1, v.end(), [](node a, node b){
        return a.w < b.w;
    });
    for (int i = 1; i<=n; i++){
        pre[i] = pre[i-1]+v[i].w*v[i].l;
        ti[i] = ti[i-1]+v[i].l;
    }
    vector<SB> sb(n+2);
    for (int i = n; i>=1; i--){
        int tp = v[i].r-v[i].l;
        sb[i].ti = sb[i+1].ti+tp;
        sb[i].tal = sb[i+1].tal + tp*v[i].w;
    }
    for (int i = 1; i<=n; i++){
        int val = pre[n]- v[i].w*v[i].l;
        int tt = ti[n]-v[i].l;
        tt = m-tt;
        int l = i, r = n+1;
        while (l+1 < r){
            int mid = (l+r)/2;
            if (sb[mid].ti <= tt){
                r = mid;
            }
            else{
                l = mid;
            }
        }
        if(r!=i+1){
            val += sb[r].tal;
            int pp = tt-sb[r].ti;
            val +=v[r-1].w*pp;
        }else{
            val += sb[r].tal;
            int pp = tt-sb[r].ti;
            val += v[i].w * pp;
        }
        ma = max(ma, val);
    }
    cout << ma << endl;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

J

思路:大型模拟题,按充电桩充的对应电池开始用电肯定是最好的,并且到一个充电桩就应该用下个对应的电池,此题用到了set<pair<int,int>>的数据结构会很容易,因为它能对键排序和去重

就不多介绍了,代码如下:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define vi vector<int>
#define vvi vector<vi>
#define pb push_back
#define mp make_pair
#define endl '\n'
#define fo(i,l,r) for(int i=(l);i<=(r);++i)
const int N = 2e5+10;
const int mod = 1e9+7;
const int INF = 1e18;
int a[N], now[N], pos[N], xi[N], fir[N], lst[N], nxt[N];
void solve()
{
    int n, m, k;
    cin >> n >> m;
    fo(i,1,n) cin >> a[i], now[i] = a[i];
    fo(i,1,m) cin >> pos[i] >> xi[i];
    fo(i,1,n) fir[i] = m+1, lst[i] = 0;
    fo(i,1,m){
        if (fir[xi[i]] > m) fir[xi[i]] = i;
        if (lst[xi[i]]) nxt[lst[xi[i]]] = i;
        lst[xi[i]] = i;
    }
    for (int i = 1; i<=n; i++) if (lst[i]) nxt[lst[i]] = m+1;
    set<pair<int, int>> s;
    for (int i = 1; i<=n; i++) s.insert(pair<int, int>(fir[i], i));
    int sum = 0;
    for (int i = 1; i<=m; i++){
        int r = pos[i]-pos[i-1];
        while (!s.empty() && now[s.begin()->second] <= r){
            int k = s.begin()->second;
            sum += now[k];
            r -= now[k];
            now[k]=0;
            s.erase(s.begin());
        }
        if (s.empty() && r > 0) {
            cout << sum << endl;
            return ;
        }
        now[s.begin()->second] -= r;
        sum += r;
        now[xi[i]] = a[xi[i]];
        if (s.find(pair<int, int>(i, xi[i]))!= s.end()) s.erase(pair<int, int>(i, xi[i]));
        s.insert(pair<int, int>(nxt[i], xi[i]));
    }
    for (int i = 1; i<=n; i++) sum += now[i];
    cout << sum << endl;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }
    
    return 0;
}

L

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define int long long
const int N=1e5+11;
const LL MOD=998244353;
int n;
vector<int> e[N];
LL dp[N][3];//dp[u][i]表示u节点子树内所有到u的边非空“半路径”的i次项的和
LL sz[N],ss[N];
LL ksm(LL a,LL x){//求逆元
    LL ret=1;
    a=(a%MOD+MOD)%MOD;
    while(x){
        if(x&1)ret=ret*a%MOD;
        a=a*a%MOD;
        x>>=1;
    }
    return ret;
}
void dfs_pre(int now,int fa){//求k
    sz[now]=1;
    ss[now]=0;
    for(int to:e[now]){
        if(to==fa)continue;
        dfs_pre(to,now);
        sz[now]+=sz[to];
        ss[now]+=sz[to]*sz[to]%MOD;
    }
    ss[now]%=MOD;
}
void dfs(int now,int fa,LL &ans){
    memset(dp[now],0,sizeof(dp[now]));
    for(int to:e[now]){
        if(to==fa)continue;
        dfs(to,now,ans);
        LL t[3];
        t[0]=dp[to][0];
        t[1]=(dp[to][1]+dp[to][0])%MOD;
        t[2]=(dp[to][2]+2ll*dp[to][1]+dp[to][0])%MOD;//t[0...3]所有半路径的长度+1
        for(int i=0;i<3;++i){//加入v到u的“半路径”
            t[i]=(t[i]+sz[to]*sz[to]%MOD-ss[to])%MOD;
        }
        LL tmp=t[0]*dp[now][2]%MOD+dp[now][0]*t[2]%MOD+2ll*t[1]*dp[now][1]%MOD;
        tmp+=((n-sz[to])*(n-sz[to])%MOD-ss[now]+sz[to]*sz[to]%MOD-(n-sz[now])*(n-sz[now])%MOD)%MOD*t[2];
        ans+=tmp%MOD;
        for(int i=0;i<3;++i){
            dp[now][i]=(dp[now][i]+t[i])%MOD;
        }
    }
}
void work(){
    cin>>n;
    for(int i=1;i<=n;++i){
        e[i].clear();
    }
    for(int i=0;i<n-1;++i){
        int u,v;
        cin>>u>>v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    LL ans=0;
    dfs_pre(1,0);
    dfs(1,0,ans);
    ans=(ans%MOD+MOD)%MOD;
    LL ii=ksm(1ll*n*(n-1)/2ll,MOD-2);
    cout<<ans*ii%MOD*ii%MOD<<"\n";
}
signed main(){
    int T;
    cin>>T;
    while(T--){
        work();
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wirepuller_king

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值