CSP-S 2025题解

P14361 [CSP-S 2025] 社团招新 / club(民间数据)

题目背景

民间数据测试强度可能偏弱。

题目描述

小 L 是学校算法协会的成员。在今年的学校社团招新中,小 L 一共招收了 nnn 个新成员,其中 nnn偶数。现在小 L 希望将他们分到协会不同的部门。

算法协会共设有三个部门,其中第 iii (1≤i≤n1 \leq i \leq n1in) 个新成员对第 jjj (1≤j≤31 \leq j \leq 31j3) 个部门的满意度为 ai,ja_{i,j}ai,j。定义一个分配方案的满意度为所有新成员对分配到的部门的满意度之和,也就是说,若将第 iii (1≤i≤n1 \leq i \leq n1in) 个新成员分配到了第 di∈{1,2,3}d_i \in \{1,2,3\}di{1,2,3} 个部门,则该分配方案的满意度为 ∑i=1nai,di\sum_{i=1}^{n} a_{i,d_i}i=1nai,di

小 L 不希望某一个部门的新成员数量过多。具体地,他要求在分配方案中,不存在一个部门被分配多于 n2\frac{n}{2}2n 个新成员。你需要帮助小 L 求出,满足他要求的分配方案的满意度的最大值。

输入格式

本题包含多组测试数据。

输入的第一行包含一个正整数 ttt,表示测试数据组数。

接下来依次输入每组测试数据,对于每组测试数据:

  • 第一行包含一个正整数 nnn,表示新成员的数量。
  • i+1i+1i+1 (1≤i≤n1 \leq i \leq n1in) 行包含三个非负整数 ai,1,ai,2,ai,3a_{i,1}, a_{i,2}, a_{i,3}ai,1,ai,2,ai,3,分别表示第 iii 个新成员对第 1,2,31,2,31,2,3 个部门的满意度。

输出格式

对于每组测试数据,输出一行一个非负整数,表示满足小 L 要求的分配方案的满意度的最大值。

输入输出样例 #1

输入 #1

3
4
4 2 1
3 2 4
5 3 4
3 5 1
4
0 1 0
0 1 0
0 2 0
0 2 0
2
10 9 8
4 0 0

输出 #1

18
4
13

说明/提示

【样例 1 解释】

该样例共包含三组测试数据。

对于第一组测试数据,可以将四个新成员分别分配到第 1,3,1,21,3,1,21,3,1,2 个部门,则三个部门的新成员数量分别为 2,1,12,1,12,1,1,均不超过 42=2\frac{4}{2} = 224=2,满意度为 4+4+5+5=184 + 4 + 5 + 5 = 184+4+5+5=18

对于第二组测试数据,可以将四个新成员分别分配到第 1,1,2,21,1,2,21,1,2,2 个部门,则三个部门的新成员数量分别为 2,2,02,2,02,2,0,均不超过 42=2\frac{4}{2} = 224=2,满意度为 0+0+2+2=40 + 0 + 2 + 2 = 40+0+2+2=4

对于第三组测试数据,可以将两个新成员分别分配到第 2,12,12,1 个部门,则三个部门的新成员数量分别为 1,1,01,1,01,1,0,均不超过 22=1\frac{2}{2} = 122=1,满意度为 9+4=139 + 4 = 139+4=13

【样例 2】

见选手目录下的 club/club2.in\textbf{\textit{club/club2.in}}club/club2.inclub/club2.ans\textbf{\textit{club/club2.ans}}club/club2.ans

该样例满足测试点 3,43,43,4 的约束条件。

【样例 3】

见选手目录下的 club/club3.in\textbf{\textit{club/club3.in}}club/club3.inclub/club3.ans\textbf{\textit{club/club3.ans}}club/club3.ans

该样例满足测试点 5∼85 \sim 858 的约束条件。

【样例 4】

见选手目录下的 club/club4.in\textbf{\textit{club/club4.in}}club/club4.inclub/club4.ans\textbf{\textit{club/club4.ans}}club/club4.ans

该样例满足测试点 999 的约束条件。

【样例 5】

见选手目录下的 club/club5.in\textbf{\textit{club/club5.in}}club/club5.inclub/club5.ans\textbf{\textit{club/club5.ans}}club/club5.ans

该样例满足测试点 15,1615,1615,16 的约束条件。

【数据范围】

对于所有测试数据,保证:

  • 1≤t≤51 \leq t \leq 51t5;
  • 2≤n≤1052 \leq n \leq 10^52n105,且 nnn 为偶数;
  • 对于所有 1≤i≤n1 \leq i \leq n1in1≤j≤31 \leq j \leq 31j3,均有 0≤ai,j≤2×1040 \leq a_{i,j} \leq 2 \times 10^40ai,j2×104

::cute-table{tuack}

测试点编号n=n=n=特殊性质
111222
222444^
3,43, 43,4101010^
5∼85 \sim 858303030^
999200200200B
10,1110, 1110,11^
12121210510^5105A
13,1413, 1413,14^B
15,1615, 1615,16^C
17∼2017 \sim 201720^

特殊性质 A:对于所有 1≤i≤n1 \leq i \leq n1in,均有 ai,2=ai,3=0a_{i,2} = a_{i,3} = 0ai,2=ai,3=0

特殊性质 B:对于所有 1≤i≤n1 \leq i \leq n1in,均有 ai,3=0a_{i,3} = 0ai,3=0

特殊性质 C:对于所有 1≤i≤n1 \leq i \leq n1in1≤j≤31 \leq j \leq 31j3ai,ja_{i,j}ai,j 均在 [0,2×104][0, 2 \times 10^4][0,2×104] 中独立均匀随机生成。

思路

首先,想到暴力枚举,显然只有20分。
随后,可以发现,该题能用贪心解决,首先贪心取最优,若超限,则直接贪心分割代价最小的几个即可。

代码见下

#include<bits/stdc++.h>
using namespace std;
long long t,n,a[100005][4],b[100005],op=0,c[4],d[100005],sd=0;
int main(){
	cin>>t;
    while(t--){
        cin>>n;
        op=0;
        c[1]=c[2]=c[3]=0;
        for(int i=1;i<=n;i++){
            cin>>a[i][1]>>a[i][2]>>a[i][3];
            if(a[i][1]>=a[i][2]&&a[i][1]>=a[i][3]){
                op+=a[i][1];
                b[i]=1;
                c[1]++;
            }
            else if(a[i][2]>=a[i][1]&&a[i][2]>=a[i][3]){
                op+=a[i][2];
                b[i]=2;
                c[2]++;
            }
            else{
                op+=a[i][3];
                b[i]=3;
                c[3]++;                
            }
        }
        sd=0;
        if(c[1]>=n/2+1){
            for(int i=1;i<=n;i++){
                if(b[i]==1){
                    d[++sd]=a[i][1]-max(a[i][2],a[i][3]);
                }
            }
            sort(d+1,d+sd+1);
            for(int i=1;i<=sd-n/2;i++){
                op-=d[i];
            }
            cout<<op<<endl;
        }
        else if(c[2]>=n/2+1){
            for(int i=1;i<=n;i++){
                if(b[i]==2){
                    d[++sd]=a[i][2]-max(a[i][1],a[i][3]);
                }
            }
            sort(d+1,d+sd+1);
            for(int i=1;i<=sd-n/2;i++){
                op-=d[i];
            }
            cout<<op<<endl;            
        }
        else if(c[3]>=n/2+1){
            for(int i=1;i<=n;i++){
                if(b[i]==3){
                    d[++sd]=a[i][3]-max(a[i][1],a[i][2]);
                }
            }
            sort(d+1,d+sd+1);
            for(int i=1;i<=sd-n/2;i++){
                op-=d[i];
            }
            cout<<op<<endl;            
        }
        else{
            cout<<op<<endl;
        }
    }
	return 0;
}

P14362 [CSP-S 2025] 道路修复 / road(民间数据)

题目背景

由于评测机性能差异,本题时限提升 1 秒。

测试数据未经过强度检验,可能存在错误做法获得较高分数,仅供参考。

  • 2025.11.03 11:40:更新了一部分测试数据,可以卡掉部分时间复杂度 ∼O(m2k)\sim O(m2^k)O(m2k) 的做法。
  • 11:56:修复上述数据存在不连通和重边的问题。

题目描述

C 国的交通系统由 nnn 座城市与 mmm 条连接两座城市的双向道路构成,第 iii (1≤i≤m1 \leq i \leq m1im) 条道路连接城市 uiu_iuiviv_ivi任意两座城市都能通过若干条道路相互到达。

然而,近期由于一场大地震,所有 mmm 条道路都被破坏了,修复第 iii (1≤i≤m1 \leq i \leq m1im) 条道路的费用为 wiw_iwi。与此同时,C 国还有 kkk 个准备进行城市化改造的乡镇。对于第 jjj (1≤j≤k1 \leq j \leq k1jk) 个乡镇,C 国对其进行城市化改造的费用为 cjc_jcj。在城市化改造完第 jjj (1≤j≤k1 \leq j \leq k1jk) 个乡镇后,可以在这个乡镇与原来的 nnn 座城市间建造若干条道路,其中在它与第 iii (1≤i≤n1 \leq i \leq n1in) 座城市间建造一条道路的费用为 aj,ia_{j,i}aj,i。C 国可以在这 kkk 个乡镇中选择任意多个进行城市化改造,也可以不选择任何乡镇进行城市化改造。

为尽快恢复城市间的交通,C 国政$ $府希望以最低的费用将原有nnn 座城市两两连通,也即任意两座原有的城市都能通过若干条修复或新建造的道路相互到达。你需要帮助他们求出,将原有的 nnn 座城市两两连通的最小费用。

输入格式

输入的第一行包含三个非负整数 n,m,kn, m, kn,m,k,分别表示原有的城市数量、道路数量和准备进行城市化改造的乡镇数量。

输入的第 i+1i+1i+1 (1≤i≤m1 \leq i \leq m1im) 行包含三个非负整数 ui,vi,wiu_i, v_i, w_iui,vi,wi,表示第 iii 条道路连接的两座城市与修复该道路的费用。

输入的第 j+m+1j+m+1j+m+1 (1≤j≤k1 \leq j \leq k1jk) 行包含 n+1n+1n+1 个非负整数 cj,aj,1,aj,2,…,aj,nc_j, a_{j,1}, a_{j,2}, \ldots, a_{j,n}cj,aj,1,aj,2,,aj,n,分别表示将第 jjj 个乡镇进行城市化改造的费用与在该乡镇与原有的城市间建造道路的费用。

输出格式

输出一行一个非负整数,表示将原有的 nnn 座城市两两连通的最小费用。

输入输出样例 #1

输入 #1

4 4 2
1 4 6
2 3 7
4 2 5
4 3 4
1 1 8 2 4
100 1 3 2 4

输出 #1

13

说明/提示

【样例 1 解释】

C 国政$ $府可以选择修复第 333 条和第 444 条道路,然后将第 111 个乡镇进行城市化改造,并建造它与第 1,31,31,3 座城市间的道路,总费用为 5+4+1+1+2=135 + 4 + 1 + 1 + 2 = 135+4+1+1+2=13。可以证明,不存在比 131313 更小的费用能使原有的 444 座城市两两连通。

【样例 2】

见选手目录下的 road/road2.inroad/road2.inroad/road2.inroad/road2.ansroad/road2.ansroad/road2.ans

该样例满足测试点 11,1211,1211,12 的约束条件。

【样例 3】

见选手目录下的 road/road3.inroad/road3.inroad/road3.inroad/road3.ansroad/road3.ansroad/road3.ans

该样例满足测试点 13,1413,1413,14 的约束条件。

【样例 4】

见选手目录下的 road/road4.inroad/road4.inroad/road4.inroad/road4.ansroad/road4.ansroad/road4.ans

该样例满足测试点 15,1615,1615,16 的约束条件。

【数据范围】

对于所有测试数据,保证:

  • 1≤n≤1041 \leq n \leq 10^41n1041≤m≤1061 \leq m \leq 10^61m1060≤k≤100 \leq k \leq 100k10;
  • 对于所有 1≤i≤m1 \leq i \leq m1im,均有 1≤ui,vi≤n1 \leq u_i, v_i \leq n1ui,vin, ui≠viu_i \neq v_iui=vi0≤wi≤1090 \leq w_i \leq 10^90wi109;
  • 对于所有 1≤j≤k1 \leq j \leq k1jk,均有 0≤cj≤1090 \leq c_j \leq 10^90cj109;
  • 对于所有 1≤j≤k1 \leq j \leq k1jk1≤i≤n1 \leq i \leq n1in, 均有 0≤aj,i≤1090 \leq a_{j,i} \leq 10^90aj,i109;
  • 任意两座原有的城市都能通过若干条原有的道路相互到达。

::cute-table{tuack}

测试点编号n≤n \leqnm≤m \leqmk≤k \leqk特殊性质
1∼41 \sim 41410410^410410610^6106000
5,65, 65,610310^310310510^5105555A
7,87, 87,8^^^
9,109, 109,10^10610^6106^A
11,1211, 1211,12^^^
13,1413, 1413,14^^101010A
15,1615, 1615,16^^^
17,1817, 1817,1810410^4104^555A
19,2019, 2019,20^^^
21∼2521 \sim 252125^^101010^

特殊性质 A:对于所有 1≤j≤k1 \leq j \leq k1jk,均有 cj=0c_j = 0cj=0 且均存在 1≤i≤n1 \leq i \leq n1in 满足 aj,i=0a_{j,i} = 0aj,i=0

思路

首先容易想到直接暴力枚举加点,可以发现,即使加点,可最小生成树外边必不选,所以,直接暴力。

暴力代码

#include<bits/stdc++.h>
using namespace std;
long long n,m,k,c[13],a[13][10024],bk=0,ed=0,f[10024],xx,yy,op=0,m2=0,m3=0;
struct one{
    int u,v;
    int w;
}b[2000006],b2[1000005],b3[2000005];
bool cmp(one a1,one b1){
    return a1.w<b1.w;
}
inline int find(int a1){
    if(a1==f[a1]){
        return a1;
    }
    else{
        f[a1]=find(f[a1]);
        return f[a1];
    }
}
bool bo[100005];
inline void abc(int a1){
    if(a1==k+1){
        long long op2=0,uz=0,us=0;
        m3=m2;
        for(int i=1;i<=m2;i++){
            b3[i]=b2[i];
        }
        for(int i=1;i<=k;i++){
            if(bo[i]==1){
                us++;
                op2+=c[i];
                for(int j=1;j<=n;j++){
                    ++m3;
                    b3[m3].u=i+n;
                    b3[m3].v=j;
                    b3[m3].w=a[i][j];
                }  
            }
        }
        sort(b3+1,b3+m3+1,cmp);
        for(int i=1;i<=n+k;i++){
            f[i]=i;
        }
        for(int i=1;i<=m3;i++){
            xx=find(b3[i].u);
            yy=find(b3[i].v);
            if(xx!=yy){
                f[yy]=xx;
                uz++;
                op2+=b3[i].w;
            }
            if(op2>op||uz==n+us-1){
                break;
            }
        }
        //cout<<op2<<endl;
        op=min(op,op2);
        return ;
    }
    else{
        bo[a1]=1;
        abc(a1+1);
        bo[a1]=0;
        abc(a1+1);
        return ;
    }
}
int main(){
	//freopen("road4.in","r",stdin);
	cin>>n>>m>>k;
    for(int i=1;i<=m;i++){
        scanf("%ld%ld%lld",&b[i].u,&b[i].v,&b[i].w);
    }
    bk=0;
    for(int i=1;i<=k;i++){
        cin>>c[i];
        if(c[i]!=0){
            bk=1;
        }
        ed=0;
        for(int j=1;j<=n;j++){
            scanf("%lld",&a[i][j]);
            if(a[i][j]==0){
                ed=1;
            }
        }
        if(ed==0){
            bk=1;
        }
    }
    if(bk!=1){
        for(int i=1;i<=k;i++){
            for(int j=1;j<=n;j++){
                ++m;
                b[m].u=i+n;
                b[m].v=j;
                b[m].w=a[i][j];
            }
        }
        sort(b+1,b+m+1,cmp);
        for(int i=1;i<=n+k;i++){
            f[i]=i;
        }
        for(int i=1;i<=m;i++){
            xx=find(b[i].u);
            yy=find(b[i].v);
            if(xx!=yy){
                f[yy]=xx;
                op+=b[i].w;
            }
        }
        cout<<op<<endl;
    }
    else{
        sort(b+1,b+m+1,cmp);
        for(int i=1;i<=n;i++){
            f[i]=i;
        }
        for(int i=1;i<=m;i++){
            xx=find(b[i].u);
            yy=find(b[i].v);
            if(xx!=yy){
                f[yy]=xx;
                b2[++m2]=b[i];
                op+=b[i].w;
            }
        }
        abc(1);
        cout<<op<<endl;        
    }
	return 0;
}

发现过不了,考虑优化排序,直接预处理离散化然后桶排序即可。

代码见下

#include<bits/stdc++.h>
using namespace std;
int n,m,k,c[13],a[13][10024],a2[13][10024],bk=0,ed=0,f[10024],xx,yy,js[200005],qd=0;
long long op=0,m2=0,m3=0;
struct one{
    int u,v;
    int w,w2;
}b[2000006],b2[1000005],b3[2000005];
bool cmp(one a1,one b1){
    return a1.w<b1.w;
}
inline int find(int a1){
    if(a1==f[a1]){
        return a1;
    }
    else{
        f[a1]=find(f[a1]);
        return f[a1];
    }
}
bool bo[100005];
vector<one> v[200005];
inline void abc(int a1){
    if(a1==k+1){
        long long op2=0,uz=0,us=0;
        m3=0;
        for(int i=1;i<=qd;i++){
            v[i].clear();
        }
        for(int i=1;i<=m2;i++){
            v[b2[i].w2].push_back(b2[i]);
        }        
        for(int i=1;i<=k;i++){
            if(bo[i]==1){
                us++;
                op2+=c[i];
                for(int j=1;j<=n;j++){
                    v[a2[i][j]].push_back({i+n,j,a[i][j],a2[i][j]});
                }  
            }
        }
        for(int i=1;i<=qd;i++){
            for(int j=0;j<v[i].size();j++){
                b3[++m3]=v[i][j];
            }
        }
        for(int i=1;i<=n+k;i++){
            f[i]=i;
        }
        for(int i=1;i<=m3;i++){
            xx=find(b3[i].u);
            yy=find(b3[i].v);
            if(xx!=yy){
                f[yy]=xx;
                uz++;
                op2+=b3[i].w;
            }
            if(op2>op||uz==n+us-1){
                break;
            }
        }
        //cout<<op2<<endl;
        op=min(op,op2);
        return ;
    }
    else{
        bo[a1]=1;
        abc(a1+1);
        bo[a1]=0;
        abc(a1+1);
        return ;
    }
}
map<int,int> mp;
int main(){
	//freopen("road4.in","r",stdin);
	cin>>n>>m>>k;
    for(int i=1;i<=m;i++){
        scanf("%ld%ld%lld",&b[i].u,&b[i].v,&b[i].w);
    }
    bk=0;
    for(int i=1;i<=k;i++){
        cin>>c[i];
        if(c[i]!=0){
            bk=1;
        }
        ed=0;
        for(int j=1;j<=n;j++){
            scanf("%lld",&a[i][j]);
            js[++js[0]]=a[i][j];
            if(a[i][j]==0){
                ed=1;
            }
        }
        if(ed==0){
            bk=1;
        }
    }
    if(bk!=1){
        for(int i=1;i<=k;i++){
            for(int j=1;j<=n;j++){
                ++m;
                b[m].u=i+n;
                b[m].v=j;
                b[m].w=a[i][j];
            }
        }
        sort(b+1,b+m+1,cmp);
        for(int i=1;i<=n+k;i++){
            f[i]=i;
        }
        for(int i=1;i<=m;i++){
            xx=find(b[i].u);
            yy=find(b[i].v);
            if(xx!=yy){
                f[yy]=xx;
                op+=b[i].w;
            }
        }
        cout<<op<<endl;
    }
    else{
        sort(b+1,b+m+1,cmp);
        for(int i=1;i<=n;i++){
            f[i]=i;
        }
        for(int i=1;i<=m;i++){
            xx=find(b[i].u);
            yy=find(b[i].v);
            if(xx!=yy){
                f[yy]=xx;
                b2[++m2]=b[i];
                op+=b[i].w;
                js[++js[0]]=b[i].w;
            }
        }
        sort(js+1,js+js[0]+1);
        for(int i=1;i<=js[0];i++){
            if(i==1||js[i]!=js[i-1]){
                mp[js[i]]=++qd;
            }
        }
        for(int i=1;i<=k;i++){
            for(int j=1;j<=n;j++){
                a2[i][j]=mp[a[i][j]];
            }
        }
        for(int i=1;i<=m2;i++){
            b2[i].w2=mp[b2[i].w];
        }
        abc(1);
        cout<<op<<endl;        
    }
	return 0;
}

P14363 [CSP-S 2025] 谐音替换 / replace(民间数据)

题目背景

由于评测机性能差异,本题时限提升 1 秒。

测试数据未经过检验,可能存在问题,或者可能存在错误做法获得较高分数,仅供参考。

  • 2025.11.02 10:53 民间数据更新:增加了少量 ∣t1∣≠∣t2∣|t_1| \neq |t_2|t1=t2 的测试点。
  • 2025.11.02 10:59 民间数据更新:降低了直接输出 000 的得分。

题目描述

小 W 是一名喜欢语言学的算法竞赛选手。在语言学中,谐音替换是指将原有的字词替换为读音相同或相近的字词。小 W 发现,谐音替换的过程可以用字符串来进行描述。具体地,小 W 将谐音替换定义为以下字符串问题:

给定 nnn 个字符串二元组,第 iii (1≤i≤n1 \leq i \leq n1in) 个字符串二元组为 (si,1,si,2)(s_{i,1}, s_{i,2})(si,1,si,2),满足 ∣si,1∣=∣si,2∣|s_{i,1}| = |s_{i,2}|si,1=si,2,其中 ∣s∣|s|s 表示字符串 sss 的长度。

对于字符串 sss,定义 sss替换如下:

  • 对于 sss 的某个子串 yyy,若存在 1≤i≤n1 \leq i \leq n1in 满足 y=si,1y = s_{i,1}y=si,1,则将 yyy 替换为 y′=si,2y' = s_{i,2}y=si,2。具体地,设 s=x+y+zs = x + y + zs=x+y+z,其中 xxxzzz 可以为空,“+” 表示字符串拼接,则 sss 的替换将得到字符串 s′=x+y′+zs' = x + y' + zs=x+y+z

小 W 提出了 qqq 个问题,第 jjj (1≤j≤q1 \leq j \leq q1jq) 个问题会给定两个不同的字符串 tj,1,tj,2t_{j,1}, t_{j,2}tj,1,tj,2,她想知道有多少种字符串 tj,1t_{j,1}tj,1 的替换能够得到字符串 tj,2t_{j,2}tj,2。两种 sss 的替换不同当且仅当子串 yyy 的位置不同或用于替换的二元组 (si,1,si,2)(s_{i,1}, s_{i,2})(si,1,si,2) 不同,即 x,zx, zx,z 不同或 iii 不同。你需要回答小 W 提出的所有问题。

输入格式

输入的第一行包含两个正整数 n,qn, qn,q,分别表示字符串二元组的数量和小 W 提出的问题的数量。

输入的第 i+1i+1i+1 (1≤i≤n1 \leq i \leq n1in) 行包含两个字符串 si,1,si,2s_{i,1}, s_{i,2}si,1,si,2,表示第 iii 个字符串二元组。

输入的第 j+n+1j+n+1j+n+1 (1≤j≤q1 \leq j \leq q1jq) 行包含两个字符串 tj,1,tj,2t_{j,1}, t_{j,2}tj,1,tj,2,表示小 W 提出的第 jjj 个问题。

输出格式

输出 qqq 行,其中第 jjj (1≤j≤q1 \leq j \leq q1jq) 行包含一个非负整数,表示替换后得到字符串 tj,2t_{j,2}tj,2 的字符串 tj,1t_{j,1}tj,1 的替换的数量。

输入输出样例 #1

输入 #1

4 2
xabcx xadex
ab cd
bc de
aa bb
xabcx xadex
aaaa bbbb

输出 #1

2
0

输入输出样例 #2

输入 #2

3 4
a b
b c
c d
aa bb
aa b
a c
b a

输出 #2

0
0
0
0

说明/提示

【样例 1 解释】

对于小 W 的第一个询问,共有 222t1,1t_{1,1}t1,1 的替换能够得到 t1,2t_{1,2}t1,2:

  1. x,zx, zx,z 均为空串,y=xabcxy = \text{xabcx}y=xabcx, i=1i = 1i=1,则 y′=xadexy' = \texttt{xadex}y=xadex,替换后得到 xadex\text{xadex}xadex
  2. x=xax = \texttt{xa}x=xa, y=bcy = \texttt{bc}y=bc, z=xz = \texttt{x}z=x, i=3i = 3i=3,则 y′=dey' = \texttt{de}y=de,替换后得到 xadex\texttt{xadex}xadex

【样例 3】

见选手目录下的 replace/replace3.inreplace/replace3.inreplace/replace3.inreplace/replace3.ansreplace/replace3.ansreplace/replace3.ans

该样例满足测试点 11, 12 的约束条件。

【样例 4】

见选手目录下的 replace/replace4.inreplace/replace4.inreplace/replace4.inreplace/replace4.ansreplace/replace4.ansreplace/replace4.ans

该样例满足测试点 15, 16 的约束条件。

【数据范围】

L1=∑i=1n∣si,1∣+∣si,2∣L_1 = \sum_{i=1}^{n} |s_{i,1}| + |s_{i,2}|L1=i=1nsi,1+si,2, L2=∑j=1q∣tj,1∣+∣tj,2∣L_2 = \sum_{j=1}^{q} |t_{j,1}| + |t_{j,2}|L2=j=1qtj,1+tj,2。对于所有测试数据,保证:

  • 1≤n,q≤2×1051 \leq n, q \leq 2 \times 10^51n,q2×105;
  • 2≤L1,L2≤5×1062 \leq L_1, L_2 \leq 5 \times 10^62L1,L25×106;
  • 对于所有 1≤i≤n1 \leq i \leq n1in, si,1,si,2s_{i,1}, s_{i,2}si,1,si,2 均仅包含小写英文字母,且 ∣si,1∣=∣si,2∣|s_{i,1}| = |s_{i,2}|si,1=si,2;
  • 对于所有 1≤j≤q1 \leq j \leq q1jq, tj,1,tj,2t_{j,1}, t_{j,2}tj,1,tj,2 均仅包含小写英文字母,且 tj,1≠tj,2t_{j,1} \neq t_{j,2}tj,1=tj,2

::cute-table{tuack}

测试点编号n,q≤n, q \leqn,qL1,L2≤L_1, L_2 \leqL1,L2特殊性质
1,21, 21,210210^2102200200200
3∼53 \sim 53510310^31032 0002\,0002000^
666^10610^6106AB
7,87, 87,810410^4104^A
9,109, 109,102×1052 \times 10^52×105^B
11,1211, 1211,12^2×1062 \times 10^62×106
13,1413, 1413,14^5×1065 \times 10^65×106A
15,1615, 1615,16^^B
17∼2017 \sim 201720^^

特殊性质 A:q=1q = 1q=1

特殊性质 B:定义字符串 sss特别的,当且仅当字符串 sss 仅包含字符 aaabbb,且字符 bbbsss 中出现恰好一次。对于所有 1≤i≤n1 \leq i \leq n1in, si,1,si,2s_{i,1}, s_{i,2}si,1,si,2 均为特别的,且对于所有 1≤j≤q1 \leq j \leq q1jq, tj,1,tj,2t_{j,1}, t_{j,2}tj,1,tj,2 均为特别的。

思路

首先,暴力显然不可,所以,我们可以先提取每个输入的前后缀不同开始之处的区间,然后用vector存不同,map存储vector编号,然后直接对比,提取t串的前后缀不同开始之处的区间,进行前后缀对比。

暴力代码

#include<bits/stdc++.h>
using namespace std;
int n,q,sd=0,op=0,qp=0,hp=0,os=0,oi=0,bk=0,ioi=0;
struct one{
    string s1,s2,s3,s4;
    int q,h;
}a[200005];
struct two{
    string s1,s2;
};
vector<two> v[200005];
map<string,int> mp;
string t1,t2,tt1,tt2;
int main(){
	cin>>n>>q;
    sd=0;
    bk=0;
    for(int i=1;i<=n;i++){
        cin>>a[i].s1>>a[i].s2;
        a[i].q=a[i].h=0;
        ioi=0;
        for(int j=0;j<a[i].s1.size();j++){
            if(a[i].s1[j]==a[i].s2[j]){
                a[i].q++;
            }
            else{
                break;
            }
        }
        for(int j=a[i].s1.size()-1;j>=0;j--){
            if(a[i].s1[j]==a[i].s2[j]){
                a[i].h++;
            }
            else{
                break;
            }
        }
        a[i].s3=a[i].s4="";
        for(int j=a[i].q;j<a[i].s1.size()-a[i].h;j++){
            a[i].s3+=a[i].s1[j];
            a[i].s4+=a[i].s2[j];
        } 
        t1=t2="";
        for(int j=a[i].q-1;j>=0;j--){
            t1+=a[i].s1[j];
        }
        for(int j=a[i].s1.size()-a[i].h;j<a[i].s1.size();j++){
            t2+=a[i].s1[j];
        }
        if(bk==0) for(int j=0;j<a[i].s1.size();j++){
            if((a[i].s1[j]!='a'&&a[i].s1[j]!='b')||(a[i].s2[j]!='a'&&a[i].s2[j]!='b')){
                ioi=1;
                break;
            }
        }
        if(ioi==1){
            bk=1;
        }
        //cout<<a[i].s3<<" "<<a[i].s4<<" "<<a[i].q<<" "<<a[i].h<<endl;
        if(mp[(string)(a[i].s3+">"+a[i].s4)]==0){
            mp[(string)(a[i].s3+">"+a[i].s4)]=++sd;
            v[sd].push_back({t1,t2});
        }
        else{
            v[mp[(string)(a[i].s3+">"+a[i].s4)]].push_back({t1,t2});
        }
    }
    for(int i=1;i<=q;i++){
        cin>>t1>>t2;
        if(t1.size()!=t2.size()){
            cout<<0<<endl;
        }
        else{
            op=0;
            qp=hp=0;
            for(int j=0;j<t1.size();j++){
                if(t1[j]==t2[j]){
                    qp++;
                }
                else{
                    break;
                }
            }
            for(int j=t1.size()-1;j>=0;j--){
                if(t1[j]==t2[j]){
                    hp++;
                }
                else{
                    break;
                }
            }
            tt1=tt2="";
            for(int j=qp;j<t1.size()-hp;j++){
                tt1+=t1[j];
                tt2+=t2[j];
            }
            os=mp[(string)(tt1+">"+tt2)];
            //cout<<os<<endl;
            if(bk!=0){
                for(int j=0;j<v[os].size();j++){
                	//cout<<v[os][j].s1.size()<<" "<<qp<<" "<<v[os][j].s2.size()<<" "<<hp<<endl;
                    if(v[os][j].s1.size()<=qp&&v[os][j].s2.size()<=hp){
                        oi=0;
                        for(int o=0;o<v[os][j].s1.size();o++){
                            if(v[os][j].s1[o]!=t1[qp-o-1]){
                                oi=1;
                                break;
                            }
                        }
                        //cout<<oi<<endl;
                        if(oi==0){
                            for(int o=0;o<v[os][j].s2.size();o++){
                                if(v[os][j].s2[o]!=t1[t1.size()-hp+o]){
                                    oi=1;
                                    break;
                                }
                            }                            
                        }
                        if(oi==0){
                            op++;
                        }
                    }
                }
                cout<<op<<endl;
            }
            else{
                for(int j=0;j<v[os].size();j++){
                	//cout<<v[os][j].s1.size()<<" "<<qp<<" "<<v[os][j].s2.size()<<" "<<hp<<endl;
                    if(v[os][j].s1.size()<=qp&&v[os][j].s2.size()<=hp){
                        op++;
                    }
                }
                cout<<op<<endl;
            }
        }
    }
	return 0;
}

发现过不了,考虑优化,用map求有没有找过,找过直接输出即可。

代码见下

#include<bits/stdc++.h>
using namespace std;
int n,q,sd=0,op=0,qp=0,hp=0,os=0,oi=0,bk=0,ioi=0;
struct one{
    string s1,s2,s3,s4;
    int q,h;
}a[200005];
struct two{
    string s1,s2;
};
vector<two> v[200005];
map<string,int> mp;
map<string,int> mp2;
string t1,t2,tt1,tt2;
int main(){
	cin>>n>>q;
    sd=0;
    bk=0;
    for(int i=1;i<=n;i++){
        cin>>a[i].s1>>a[i].s2;
        a[i].q=a[i].h=0;
        ioi=0;
        for(int j=0;j<a[i].s1.size();j++){
            if(a[i].s1[j]==a[i].s2[j]){
                a[i].q++;
            }
            else{
                break;
            }
        }
        for(int j=a[i].s1.size()-1;j>=0;j--){
            if(a[i].s1[j]==a[i].s2[j]){
                a[i].h++;
            }
            else{
                break;
            }
        }
        a[i].s3=a[i].s4="";
        for(int j=a[i].q;j<a[i].s1.size()-a[i].h;j++){
            a[i].s3+=a[i].s1[j];
            a[i].s4+=a[i].s2[j];
        } 
        t1=t2="";
        for(int j=a[i].q-1;j>=0;j--){
            t1+=a[i].s1[j];
        }
        for(int j=a[i].s1.size()-a[i].h;j<a[i].s1.size();j++){
            t2+=a[i].s1[j];
        }
        if(bk==0) for(int j=0;j<a[i].s1.size();j++){
            if((a[i].s1[j]!='a'&&a[i].s1[j]!='b')||(a[i].s2[j]!='a'&&a[i].s2[j]!='b')){
                ioi=1;
                break;
            }
        }
        if(ioi==1){
            bk=1;
        }
        //cout<<a[i].s3<<" "<<a[i].s4<<" "<<a[i].q<<" "<<a[i].h<<endl;
        if(mp[(string)(a[i].s3+">"+a[i].s4)]==0){
            mp[(string)(a[i].s3+">"+a[i].s4)]=++sd;
            v[sd].push_back({t1,t2});
        }
        else{
            v[mp[(string)(a[i].s3+">"+a[i].s4)]].push_back({t1,t2});
        }
    }
    for(int i=1;i<=q;i++){
        cin>>t1>>t2;
        if(t1.size()!=t2.size()){
            cout<<0<<endl;
        }
        else{
            op=0;
            qp=hp=0;
            for(int j=0;j<t1.size();j++){
                if(t1[j]==t2[j]){
                    qp++;
                }
                else{
                    break;
                }
            }
            for(int j=t1.size()-1;j>=0;j--){
                if(t1[j]==t2[j]){
                    hp++;
                }
                else{
                    break;
                }
            }
            tt1=tt2="";
            for(int j=qp;j<t1.size()-hp;j++){
                tt1+=t1[j];
                tt2+=t2[j];
            }
            os=mp[(string)(tt1+">"+tt2)];
            //cout<<os<<endl;
            if(bk!=0){
                if(mp2[(string)(t1+">"+t2)]!=0){
                    cout<<mp2[(string)(t1+">"+t2)]<<endl; 
                }
                else{
                    for(int j=0;j<v[os].size();j++){
                    	//cout<<v[os][j].s1.size()<<" "<<qp<<" "<<v[os][j].s2.size()<<" "<<hp<<endl;
                        if(v[os][j].s1.size()<=qp&&v[os][j].s2.size()<=hp){
                            oi=0;
                            for(int o=0;o<v[os][j].s1.size();o++){
                                if(v[os][j].s1[o]!=t1[qp-o-1]){
                                    oi=1;
                                    break;
                                }
                            }
                            //cout<<oi<<endl;
                            if(oi==0){
                                for(int o=0;o<v[os][j].s2.size();o++){
                                    if(v[os][j].s2[o]!=t1[t1.size()-hp+o]){
                                        oi=1;
                                        break;
                                    }
                                }                            
                            }
                            if(oi==0){
                                op++;
                            }
                        }
                    }
                    mp2[(string)(t1+">"+t2)]=op;
                    cout<<op<<endl;                    
                }
            }
            else{
                if(mp2[(string)(t1+">"+t2)]!=0){
                    cout<<mp2[(string)(t1+">"+t2)]<<endl; 
                }
                else{
                    for(int j=0;j<v[os].size();j++){
                    	//cout<<v[os][j].s1.size()<<" "<<qp<<" "<<v[os][j].s2.size()<<" "<<hp<<endl;
                        if(v[os][j].s1.size()<=qp&&v[os][j].s2.size()<=hp){
                            op++;
                        }
                    }
                    mp2[(string)(t1+">"+t2)]=op;
                    cout<<op<<endl;                    
                }

            }
        }
    }
	return 0;
}

P14364 [CSP-S 2025] 员工招聘 / employ(民间数据)

题目背景

由于评测机性能差异,本题时限提升 1 秒。

测试数据未经过检验,可能存在问题,或者可能存在错误做法获得较高分数,仅供参考。

  • 2025.11.02 11:21 民间数据更新:对特殊性质 A 的数据点做了修改。

题目描述

小 Z 和小 H 想要合伙开一家公司,共有 nnn 人前来应聘,编号为 1∼n1 \sim n1n。小 Z 和小 H 希望录用至少 mmm 人。

小 H 是面试官,将在接下来 nnn 天每天面试一个人。小 Z 负责决定应聘人前来面试的顺序。具体地,小 Z 可以选择一个 1∼n1 \sim n1n 的排列 ppp,然后在第 iii (1≤i≤n1 \leq i \leq n1in) 天通知编号为 pip_ipi 的人前来面试。

小 H 准备了 nnn 套难度不一的面试题。由于 nnn 个前来应聘的人水平大致相同,因此对于同一套题,所有人的作答结果是一致的。具体地,第 iii (1≤i≤n1 \leq i \leq n1in) 天的面试题的难度为 si∈{0,1}s_i \in \{0,1\}si{0,1},其中 si=0s_i = 0si=0 表示这套题的难度较高,没有人能够做出;si=1s_i = 1si=1 表示这套题的难度较低,所有人都能做出。小 H 会根据面试者的作答结果决定是否录用,即如果面试者没有做出面试题,则会拒绝,否则会录用。

然而,每个人的耐心都有一定的上限,如果在他面试之前未录用的人数过多,则他会直接放弃参加面试。具体地,编号为 iii (1≤i≤n1 \leq i \leq n1in) 的人的耐心上限可以用非负整数 cic_ici 描述,若在他之前已经有不少于 cic_ici 人被拒绝或放弃参加面试,则他也将放弃参加面试。

小 Z 想知道一共有多少种面试的顺序 ppp 能够让他们录用至少 mmm 人。你需要帮助小 Z 求出,能够录用至少 mmm 人的排列 ppp 的数量。由于答案可能较大,你只需要求出答案对 998 244 353998\,244\,353998244353 取模后的结果。

输入格式

输入的第一行包含两个正整数 n,mn, mn,m,分别表示前来应聘的人数和希望录用的人数。

输入的第二行包含一个长度为 nnn 的字符串 s1…sns_1 \dots s_ns1sn,表示每一天的面试题的难度。

输入的第三行包含 nnn 个非负整数 c1,c2,…,cnc_1, c_2, \dots, c_nc1,c2,,cn,表示每个人的耐心上限。

输出格式

输出一行一个非负整数,表示能够录用至少 mmm 人的排列 ppp 的数量对 998 244 353998\,244\,353998244353 取模后的结果。

输入输出样例 #1

输入 #1

3 2
101
1 1 2

输出 #1

2

输入输出样例 #2

输入 #2

10 5
1101111011
6 0 4 2 1 2 5 4 3 3

输出 #2

2204128

说明/提示

【样例 1 解释】

共有以下 2 种面试的顺序 ppp 能够让小 Z 和小 H 录用至少 2 人:

  1. p=[1,2,3]p = [1,2,3]p=[1,2,3], 依次录用编号为 1 的人和编号为 3 的人;
  2. p=[2,1,3]p = [2,1,3]p=[2,1,3], 依次录用编号为 2 的人和编号为 3 的人。

【样例 3】

见选手目录下的 employ/employ3.in\textbf{\textit{employ/employ3.in}}employ/employ3.inemploy/employ3.ans\textbf{\textit{employ/employ3.ans}}employ/employ3.ans

该样例满足测试点 6 ~ 8 的约束条件。

【样例 4】

见选手目录下的 employ/employ4.in\textbf{\textit{employ/employ4.in}}employ/employ4.inemploy/employ4.ans\textbf{\textit{employ/employ4.ans}}employ/employ4.ans

该样例满足测试点 12 ~ 14 的约束条件。

【样例 5】

见选手目录下的 employ/employ5.in\textbf{\textit{employ/employ5.in}}employ/employ5.inemploy/employ5.ans\textbf{\textit{employ/employ5.ans}}employ/employ5.ans

该样例满足测试点 18 ~ 21 的约束条件。

【数据范围】

对于所有测试数据,保证:

  • 1≤m≤n≤5001 \leq m \leq n \leq 5001mn500;
  • 对于所有 1≤i≤n1 \leq i \leq n1in,均有 si∈{0,1}s_i \in \{0,1\}si{0,1};
  • 对于所有 1≤i≤n1 \leq i \leq n1in,均有 0≤ci≤n0 \leq c_i \leq n0cin

::cute-table{tuack}

测试点编号n≤n \leqnmmm特殊性质
1,21,21,2101010≤n\leq nn
3∼53 \sim 535181818^^
6∼86 \sim 86810210^2102^A
9∼119 \sim 11911^^
12∼1412 \sim 141214500500500=1=1=1^
151515^=n=n=n^
16,1716,1716,17^≤n\leq nnA
18∼2118 \sim 211821^^B
22∼2522 \sim 252225^^

特殊性质 A: 对于所有 1≤i≤n1 \leq i \leq n1in,均有 si=1s_i = 1si=1

特殊性质 B: 在 s1,s2,…,sns_1, s_2, \dots, s_ns1,s2,,sn 中最多只有 18 个取值为 1,即 ∑i=1nsi≤18\sum_{i=1}^{n} s_i \leq 18i=1nsi18

思路

DP即可,首先容易想到暴力状态压缩DP。

暴力代码

#include<bits/stdc++.h>
using namespace std;
long long n,m,c[100005],mod=998244353,f[20][300005],rm[200005],d,df=0,op=0;
char s[100005];
bool bo[100005];
int main(){
	cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>s[i];
    }
    for(int i=1;i<=n;i++){
        cin>>c[i];
    }
    rm[0]=1;
    for(int i=1;i<=18;i++){
        rm[i]=rm[i-1]*2;
    }
    if(n<=18){
        f[0][0]=1;
        d=pow(2,n)-1;
        for(int i=0;i<=n-1;i++){
            for(int j=0,k;j<=d;j++){
                //cout<<i<<" "<<j<<" "<<f[i][j]<<endl;
                k=j;
                df=0;
                for(int o=0;o<=n-1;o++){
                    if(k%2==1){
                        bo[o]=1;
                        df++;
                    }
                    else{
                        bo[o]=0;
                    }
                    k/=2;
                }
                for(int o=0;o<=n-1;o++){
                    if(bo[o]==0){
                        if(s[df+1]=='0'){
                            f[i][j+rm[o]]=(f[i][j+rm[o]]+f[i][j])%mod;
                        }
                        else{
                            if(c[o+1]<=df-i){
                                f[i][j+rm[o]]=(f[i][j+rm[o]]+f[i][j])%mod;
                            }
                            else{
                                f[i+1][j+rm[o]]=(f[i+1][j+rm[o]]+f[i][j])%mod;
                            }
                        }
                    }
                }
            }
        }
        for(int i=m;i<=n;i++){
            op=(op+f[i][d])%mod;
        }
        cout<<op<<endl;
    }
    else{
        cout<<0<<endl;
    }
	return 0;
}
posted @ 2025-11-04 19:56  bz02_2023f2  阅读(16)  评论(0)    收藏  举报  来源