ICPC2023 合肥站

B. Queue Sorting

tag: Dilworth DP

我们可以发现,我们将原序列建图,让小的在前面的数连向大的在后面的数,那么我们就可以将问题转化为:求图中的最小链覆盖是否 \(\leq 2\)

然后我们接着使用 Dilworth 定理,就变成了判断最长反链是否 \(\leq 2\)

即:序列的逆序列的最长上升子序列长度不超过 \(2\)

即求:有多少种不同的序列,它的最长上升子序列长度不超过2。

之后的话,先摆烂了

C. Cyclic Substrings

tag: PAM

回文自动机的板子,但是本人抄板子导致原本应该 \(-'0'\) 的,结果减成 \(-'a'\) 了,导致调了半天

code
#include <bits/stdc++.h>
#define endl "\n"
using namespace std;
const int NN = 6e6 + 1024;
int n;
string s;
int len[NN], fail[NN], go[NN][26], last, pam_cnt;
long long cnt[NN];
void clgo(int x){memset(go[x], 0, sizeof(go[x]));}
void init()
{
    fail[0] = pam_cnt = 1; len[1] = -1;
    last = 0; clgo(0); clgo(1);
}
int getfail(int n,int p)
{
    // cout << '1' << endl;
    while(s[n-len[p]-1] != s[n]) p = fail[p];

    return p;
}
void extend(int n)
{
    int p = getfail(n,last), c = s[n] - '0';
    if(!go[p][c])
    {
        int q = ++pam_cnt, now = p;
        clgo(q);
        len[q] = len[p] + 2;
        p = getfail(n, fail[p]);
        fail[q] = go[p][c]; last = go[now][c] = q;
    }
    else last = go[p][c];
    if(n >= ::n/2) ++cnt[last];
}
const long long MOD = 998244353;
long long ans;

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0);
    init();
    cin >> n >> s;
    s = s + s; n *= 2;
    for(int i = 0; i < n; ++i)
        extend(i);

    for(int u = pam_cnt; u >= 2; --u)
    {
        cnt[fail[u]] += cnt[u];
        if(len[u] <= n/2) ans += 1ll * cnt[u] * cnt[u] % MOD * len[u] % MOD;
        ans %= MOD;
    }
    cout << ans % MOD;
    return 0;
}

E. Matrix Distances

计算所有同色单元格之间的曼哈顿距离之和

分开 \(x,y\) 坐标分别计算即可

code
#include <bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
unordered_map<int,int>h;
vector<int>a[1000005],b[1000005];
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,m;
    cin>>n>>m;
    int tot=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            int x;
            cin>>x;
            if(h.find(x)==h.end())
            {
                h[x]=++tot;
            }
            a[h[x]].push_back(i);
            b[h[x]].push_back(j);
        }
    }
    int ans=0;
    for(int i=1;i<=tot;i++)
    {
        sort(a[i].begin(),a[i].end());
        sort(b[i].begin(),b[i].end());
        int cur=0;
        for(int j=0;j<a[i].size();j++)
        {
            ans=ans+(a[i][j]*j-cur)*2;
            cur+=a[i][j];
        }
        cur=0;
        for(int j=0;j<b[i].size();j++)
        {
            ans=ans+(b[i][j]*j-cur)*2;
            cur+=b[i][j];
        }
    }
    cout<<ans<<endl;
    return 0;
}

F. Colorful Balloons

tag: 摩尔投票

题意就是求最后是否有数量大于 \(\frac n 2\) 的元素

当然,\(map\) 也可以过

code
#include <bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
map<string,int>q;
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin>>n;
    string ans="uh-oh";
    for(int i=1;i<=n;i++)
    {
        string s;
        cin>>s;
        q[s]++;
        if(q[s]*2>n)
        {
            ans=s;
        }
    }
    cout<<ans<<endl;
    return 0;
}

G. Streak Manipulation

tag: 二分答案 DP

我们首先二分第 \(k\) 长的 \(1\dots 1\) 串的长度 \(mid\)

那么我们现在的程序就简化成为:求是否能够通过最多进行 \(m\) 次操作,使得长度大于 \(mid\)\(1\dots 1\) 串的个数 \(\geq k\)

我们设 \(f_{i,j,0/1}\) 表示 对于前 \(i\) 个数,已经找到了 \(j\) 个满足条件的串,当前位置是否是一个结尾所需要的修改次数

\[f_{i,j,1} = f_{i-x,j-1,0} + val_{i-x+1,i} \]

\[f_{i,j,0} = \min \{ f_{i-x,j-1,0}, f_{i-x,j-1,1} \} \]

复杂度:\(O(nk\log n)\)

code
#include <bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
int sum[200005];
int f[200005][6],g[200005][6];
deque<int>q[6];
int n,m,k;
string s;
bool check(int len)
{
    for(int i=0;i<=n;i++)
    {
        for(int j=0;j<=k;j++)
        {
            f[i][j]=g[i][j]=INT_MAX;
        }
    }
    for(int i=0;i<=k;i++)
    {
        q[i].clear();
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=k;j++)
        {
            if(i-len+1>=1)
            {
                while(q[j-1].size()>1)
                {
                    if(q[j-1][1]<=i-len)
                    {
                        q[j-1].pop_front();
                    }
                    else
                    {
                        break;
                    }
                }
                if(j==1)
                {
                    f[i][j]=len-(sum[i]-sum[i-len]);
                }
                else if(q[j-1].size())
                {
                    f[i][j]=g[q[j-1].front()-1][j-1]+len-(sum[i]-sum[i-len]);
                }
            }
            g[i][j]=min(g[i-1][j],f[i][j]);
            if(s[i-1]=='0')
            {
                q[j].push_back(i);
            }
        }
    }
    return g[n][k]<=m;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n>>m>>k;
    cin>>s;
    for(int i=1;i<=n;i++)
    {
        sum[i]=sum[i-1]+s[i-1]-'0';
    }
    if(check(1)==false)
    {
        cout<<-1<<endl;
        return 0;
    }
    int l=1,r=n;
    while(l<r)
    {
        int mid=(l+r+1)>>1;
        if(check(mid))
        {
            l=mid;
        }
        else
        {
            r=mid-1;
        }
    }
    cout<<l<<endl;
    return 0;
}

J. Takeout Delivering

tag: 枚举 最短路

题意就是求从 \(1\) 走到 \(n\) 的 最大和次大 的边长和最小

个人一开始想到的方法是 二分答案 + 分层图 判断连通性,但是发现对于和这个东西,我们不能很好地处理

然后就想到了枚举最长边,然后我们预处理从 起点和终点 到点 \(x\) 的所有 路径的边权最大值 的最小值

然后最后就正常合并了

code
#include <bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int NN = 3e5 + 8;
int n,m;
 
struct Edge
{
    int v,val;
    bool operator < (const Edge &x)const
    {
        return val > x.val;
    }
};
vector<Edge>a[NN];
 
int f[NN],g[NN];
void dijkstra1()
{
    priority_queue<Edge>q;
    q.push({1,0}); f[1] = 0;
    while(!q.empty())
    {
        int u = q.top().v,val = q.top().val;
        q.pop();
        if(val != f[u]) continue;
        for(auto e : a[u])
        {
            int v = e.v;
            if(f[v] > max(e.val, f[u]))
            {
                f[v] = max(e.val,f[u]);
                q.push({v,f[v]});
            }
        }
    }
}
void dijkstra2()
{
    priority_queue<Edge>q;
    q.push({n,0}); g[n] = 0;
    while(!q.empty())
    {
        int u = q.top().v,val = q.top().val;
        q.pop();
        if(val != g[u]) continue;
        for(auto e : a[u])
        {
            int v = e.v;
            if(g[v] > max(e.val, g[u]))
            {
                g[v] = max(e.val,g[u]);
                q.push({v,g[v]});
            }
        }
    }
}
signed main()
{
    ios::sync_with_stdio(false),cin.tie(0);
    memset(f,0x3f,sizeof(f)), memset(g,0x3f,sizeof(g));
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        cin>>u>>v>>w;
        a[u].push_back({v,w});
        a[v].push_back({u,w});
    }
    dijkstra1();
    dijkstra2();
    int ans = 1e18;
    for(auto e : a[1])
    {
        int v = e.v, val = e.val;
        if(v == n) ans = min(ans,val);
    }
    for(int u = 1; u <= n; ++u)
    {
        for(auto e: a[u])
        {
            int v = e.v, val = e.val;
            // if(v == 1 || v == n) continue;
            if(f[u] <= val && g[v] <= val) ans = min(ans,val + max(f[u],g[v]));
        }
    }
    // for(int i = 1; i <= n; ++i)
    // {
    //     cout << f[i] << ' ';
    // }
    // cout << '\n';
    // for(int i = 1; i <= n; ++i) cout << g[i] << ' ';
    // cout << '\n';
    cout << ans;
    return 0;
}
posted @ 2025-10-28 14:07  ricky_lin  阅读(21)  评论(0)    收藏  举报