有问题请评论区聊喵!!!

A:迷星叫

思路

直接使用判断语句模拟输出就行了。

代码展示


#include <bits/stdc++.h>
#define il inline

using namespace std;
using ll = long long;
using ull = unsigned long long;
using int128=__int128_t;

const ll N = 5e5 + 5, mod =1e9+7, inf = 2e18;
const double esp=1e-9;
double PI=3.1415926;

il void solve(){
    int n;
    cin>>n;
    if(n==3||n==6){
        cout<<"koishiYun\n";
    }else{
        cout<<"Kato_Shoko\n";
    }
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    
    int t = 1;

    cin >> t;

    while (t--) {

        solve();

    }

    return 0;
}

B:焚音打

观察

设我们选择的发布顺序为排列 ,其中第 个发布的是消息 。题目中每条消息的实际贡献为

因此总影响力为

注意到第一项 与排列无关。所以要最大化总影响力,等价于最小化

直观上,为了使带大权(即大 )的项乘以尽可能小 的系数 ,应该把 较大的消息放在前面。

实现

将所有消息按 非增(从大到小)排序;按此顺序发布即为最优。

代码展示

#include <bits/stdc++.h>
#define il inline

using namespace std;
using ll = long long;
using ull = unsigned long long;
using int128=__int128_t;

const ll N = 2e5 + 5, mod =1e9+7, inf = 2e18;
const double esp=1e-9;
double PI=3.1415926;

il void solve(){
    int n;
    cin>>n;
    vector<pair<ll,ll>>pa(n);
    for(auto &[x,y]:pa)cin>>x>>y;
    sort(pa.begin(),pa.end(),[&](auto &x,auto &y){
        return x.second>y.second;
    });
    ll ans=0;
    for(ll i=0;i<n;i++){
        auto [a,b]=pa[i];
        ans+=a-b*i;
    }
    cout<<ans<<'\n';
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    
    int t = 1;

    cin >> t;

    while (t--) {

        solve();

    }

    return 0;
}

C:诗超绊

思路简述

我们要用至多 次区间加操作(每次加同一个固定正整数 ) 使序列严格递增。显然,若能用某个 实现目标, 则对更大的 也可以实现,因此答案关于 单调的,可用二分查找最小可行的

给定 ,如何判断可行?

我们只需判断是否能用不超过 次操作,使得每个位置满足

由于每次操作将一段连续区间整体加 , 可以等价地把操作理解为“将某些前缀区间的加次数累积起来”。 我们可以贪心地从左到右判断:

  • 设当前已经进行了若干次区间加,使得到位置 为止,序列已严格递增;
  • 若此时 ,则至少需要让 增加到 , 因此需要的额外加次数为
    这些加法可视为从位置 到结尾都加上 , 操作次数总和累加上

若累计操作次数总和 ,则说明当前 可行,否则不可行。

代码展示

#include <bits/stdc++.h>
#define il inline

using namespace std;
using ll = long long;
using ull = unsigned long long;
using int128=__int128_t;

const ll N = 2e5 + 5, mod =1e9+7, inf = 2e18;
const double esp=1e-9;
double PI=3.1415926;

il void solve(){
    ll n,m;
    cin>>n>>m;
    vector<ll>a(n+1);
    for(int i=1;i<=n;i++)cin>>a[i];

    bool ok=true;
    for(int i=2;i<=n;i++){
        if(a[i]<=a[i-1]){
            ok=false;
            break;
        }
    }
    if(ok){
        cout<<0<<'\n';
        return ;
    }

    auto check=[&](ll mid)->bool{
        ll cnt=0;
        for(int i=2;i<=n;i++){
            if(a[i]>a[i-1])continue;
            ll diff=a[i-1]-a[i]+1;
            ll t=(diff+mid-1)/mid;
            cnt+=t;
            if(cnt>m)return false;
        }
        return true;
    };

    ll l=1,r=inf,ans=-1;
    while(l<=r){
        ll mid=l+r>>1;
        if(check(mid)){
            ans=mid;
            r=mid-1;
        }else{
            l=mid+1;
        }
    }
    cout<<ans<<'\n';
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    
    int t = 1;

    cin >> t;

    while (t--) {

        solve();

    }

    return 0;
}

D:春日影

等价图变换

题中规则如下:在无向图上,若丰川祥子沿同一条边 连续走两次(), 则当她回到 后的下一条移动(从 出发的任意 一条边)为免费(费用 )。此免费机会可重复使用。

考虑节点 的所有邻边权重,记

若某条最优路径在节点 处触发一次“来回”并随后 从 走向某邻居 (且用到免费机会),那么触 发往返的最优选择必然是使用权重最小的邻边(使得来 回代价 最小)。因此,把这段操作等价为先支付 (往返代价 ),然后再走一条原有的 边但该次移动视 为“免费”——在等价图上直接把它表示为一条代价为 的边从 即可(与原来的 边并存, 会选择较优的那条)。

因此可以做如下变换并求最短路:

  1. 计算每个节点 的最短邻边权
  2. 在原有无向边保持不变的基础上,对于每个节点 ,对它的每个原始邻居 增加一条有向边 ,边权为 (表示“先 在 做最短邻边往返,再从 出发到 的等价代价”)。
  3. 在此新图上直接运行一次 ,从 的距离即为答案。

代码展示

#include <bits/stdc++.h>
#define il inline

using namespace std;
using ll = long long;
using ull = unsigned long long;
using int128=__int128_t;

const ll N = 2e5 + 5, mod =1e9+7, inf = 2e18;
const double esp=1e-9;
double PI=3.1415926;

vector<pair<ll,ll>>e[N],g[N],G[N];

il auto dijk(int n){
    struct Node{
        ll id,d;
        bool operator<(const Node&a)const{
            return d>a.d;
        }
    };
    priority_queue<Node>pq;
    vector<ll>dis(n+5,inf);
    vector<bool>vis(n+5);

    pq.push({1,dis[1]=0});

    while(!pq.empty()){
        auto [u,d]=pq.top();
        pq.pop();

        if(vis[u])continue;
        vis[u]=true;

        for(auto [v,w]:g[u]){
            if(dis[u]+w<dis[v]){
                pq.push({v,dis[v]=dis[u]+w});
            }
        }
    }
    return dis;
}

il void solve(){
    ll n,m;
    cin>>n>>m;
    while(m--){
        ll u,v,w;
        cin>>u>>v>>w;
        assert(w>=0&&w<=1e9);
        e[u].push_back({v,w});
        e[v].push_back({u,w});
    }
    for(int i=1;i<=n;i++){
        sort(e[i].begin(),e[i].end(),[&](auto &x,auto &y){
            return x.second<y.second;
        });
    }

    for(int u=1;u<=n;u++){
        if(e[u].empty())continue;
        ll w=e[u][0].second*2;
        for(auto [v,ww]:e[u]){
            G[u].push_back({v,w});
        }
    }
    for(int u=1;u<=n;u++){
        g[u]=e[u];
        for(auto [v,w]:G[u]){
            g[u].push_back({v,w});
        }
    }
    auto dis=dijk(n);
    cout<<(dis[n]==inf?-1:dis[n]);
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    
    int t = 1;

    //cin >> t;

    while (t--) {

        solve();

    }

    return 0;
}


E:轮符雨

观察

任意二进制字符串对应其段长的有序正整数序列,反之,定一组段长并指定首字符(0 或 1)即可唯一恢复字符串。因此问题等价于:计数所有有序正整数序列

使得

最后把结果乘以 (首字符为 的两种选择)。

动态规划设计

为“用若干段恰好凑出总长度 且累计得分为 的段长序列的数量”。初始状态

转移:从状态 可以追加一个段长 ,其中 ,其对得分的贡献为

因此转移为

最终答案为

复杂度分析

设题中上界为 ,该方案的最差时间复杂度为

代码展示

#include <bits/stdc++.h>
#define il inline

using namespace std;
using ll = long long;
using ull = unsigned long long;
using int128=__int128_t;

const ll N = 1e5 + 5, mod =1e9+7, inf = 2e18;
const double esp=1e-9;
double PI=3.1415926;

il void solve(){
    ll n,t;
    cin>>n>>t;

    vector<vector<ll>>dp(n+5,vector<ll>(t+5));
    dp[0][0]=1;
    for(int i=0;i<n;i++){
        for(int j=0;j<=t&&j<=i*(i-1)/2;j++){
            if(!dp[i][j])continue;
            for(int l=1;i+l<=n;l++){
                ll val=j+l*(l-1)/2;
                if(val>t)break;
                dp[i+l][val]=(dp[i+l][val]+dp[i][j])%mod;
            }
        }
    }
    cout<<dp[n][t]*2%mod<<'\n';
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    
    int t = 1;

    cin >> t;

    while (t--) {

        solve();

    }

    return 0;
}

F:明弦音

:并查集父指针;

(对父亲的“势能差”),路径压缩后变为 (即节点 相对根节点 root 的势能差)。

1. 合并

  1. 查找 的根 ,计算 (此时 );
  2. 查找 的根 ,计算 (此时 );
  3. :检查 是否等于 ,若不等则存在矛盾;
  4. :需满足
    按大小合并:
  • 方式 1:把 挂到 上,令
  • 方式 2:把 挂到 上,令

2. 绝对赋值 视作约束 ,定义固定节点 (满足 );调用 即可实现该绝对赋值。

3. 差值查询

  • 的根不同:结果未知(返回 UNKNOWN);
  • 同根:答案唯一,为

三、正确性

  • 任意点到根的 值即该点相对根节点的值差;
  • 所有等式约束均转化为“根节点间相对常量”的要求;
  • 同一连通块内任意两点的差值唯一确定;
  • 不同连通块之间无约束关联,差值无法确定。

四、复杂度 并查集路径压缩 + 按大小合并:摊还时间复杂度

代码展示

#include <bits/stdc++.h>
#define il inline

using namespace std;
using ll = long long;
using ull = unsigned long long;
using int128=__int128_t;

const ll N = 5e5 + 5, mod =998244353, inf = 2e18;
const double esp=1e-9;
double PI=3.1415926;

ll fa[N],w[N];

il int find(int x){
    if(fa[x]==x)return x;
    ll da=fa[x];
    ll next_x=find(da);
    w[x]+=w[da];
    return fa[x]=next_x;
}
il void me(ll x,ll y,ll k){
    int u=find(x),v=find(y);
    fa[u]=v;
    w[u]=w[y]+k-w[x];
}

il void solve(){
    int n,q;
    cin>>n>>q;
    for(int i=0;i<=n;i++){
        fa[i]=i;
        w[i]=0;
    }
    while(q--){
        int op;
        cin>>op;
        if(op==1){
            ll u,v,k;
            cin>>u>>v>>k;

            if(find(u)!=find(v)){
                cout<<"OK\n";
                me(u,v,k);
            }else{
                if(w[u]-w[v]!=k){
                    cout<<"CONTRADICTION\n";
                }else{
                    cout<<"OK\n";
                }
            }

        }else if(op==2){
            ll u,k;
            cin>>u>>k;
            if(find(u)!=find(0)){
                me(u,0,k);
                cout<<"OK\n";
            }else{
                if(w[u]-w[0]!=k){
                    cout<<"CONTRADICTION\n";
                }else{
                    cout<<"OK\n";
                }
            }

        }else{
            ll u,v;
            cin>>u>>v;
            if(find(u)!=find(v)){
                cout<<"UNKNOWN\n";
            }else{
                cout<<w[u]-w[v]<<'\n';
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    
    int t = 1;

    cin >> t;
    
    while (t--) {

        solve();

    }

    return 0;
}

最后有什么疑问可以在评论区指出来喵!!!