【NOIP2017】SummerTraining0724

本文针对一次算法竞赛进行了详细的实战解析,包括三个问题:数列判断、字符串匹配计数及组合分配问题。通过多种方法对比,如大暴力法、Trie树、排序去重等,并给出了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这套题目100+10+0,rank21,贼菜,T1推了2hour,T2把数组类型int打成了char,10分(大暴力90,去重后100),T370分规律,后面30防AK,6k代码量……

T1

问题 A: 数列1
时间限制: 1 Sec 内存限制: 128 MB
题目描述
这里写图片描述

输入
这里写图片描述

输出
这里写图片描述

样例输入
0 1
2000000000 1
样例输出
no
yes
提示
这里写图片描述

Solution

这里写图片描述

具体看代码

CODE

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll I=1000000000;
ll a,b;

int main()
{
    while (scanf("%lld%lld",&a,&b)==2)
    {
        if (a==I)
        {
            if (b==0) printf("no\n");
            else printf("yes\n");
        }
        else 
        {
            if (abs(a)<=I || a+b==I) printf("no\n");
            else printf("yes\n");
        }
    }   
    return 0;
}

T2

问题 B: 单词
时间限制: 5 Sec 内存限制: 512 MB
题目描述
这里写图片描述

输入
这里写图片描述

输出
样例输入
4 3
xyz
xyz
zzx
xzz
样例输出
2
1
2
1
提示
这里写图片描述

Solution

做法非常多

  1. O(n^2m)大暴力,没有任何优化,90分
  2. tire树,再加入前dfs整棵树,相对于大暴力少了一些重复走的边,卡过总用时25s
  3. 排序+去重+大暴力 总用时 10s
  4. 分类,n较小时O(n^2m)大暴力,m较小时O(3^m*3^m*m),枚举集合与集合中的数,各3^m,在m位比较,非常暴力。
  5. 对4的优化,前一半用3的方法排序+去重,后一半DP

CODE

trie树

//time:25s;
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN=100005;
int tire[MAXN][3],val[MAXN];
ll ans[MAXN];
char str[MAXN];
int n,m,cnt=0;

void insert(char *s)
{
    int len=strlen(s),now=0;
    for (int i=0;i<len;i++)
    {
        int j=s[i]-'x';
        if (tire[now][j]) now=tire[now][j];
        else now=tire[now][j]=++cnt;        
    }
    val[now]++; 
}
void dfs(int now,int len,int num)
{
    if (len==m)
    {
        ans[num]+=(ll)val[now];
        return;
    }
    for (int i=0;i<3;i++)
        if (tire[now][i]) dfs(tire[now][i],len+1,num+(str[len]-'x'==i));
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    {
        scanf("%s",str);
        dfs(0,0,0); 
        insert(str);    
    }
    for (int i=0;i<=m;i++)
        printf("%lld\n",ans[i]);    
    return 0;
}

排序+去重

//time:10s;
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN=100005;
string str[MAXN],s[MAXN];
ll sum[MAXN];
int n,m,n2,num[MAXN];

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) cin>>str[i];  
    sort(str+1,str+1+n);
    for (int i=1;i<=n;i++)
        if (str[i]!=str[i-1]) 
            s[++n2]=str[i],num[n2]=1;
        else num[n2]++;
    for (int i=1;i<=n2;i++) sum[m]+=(ll)(num[i]-1)*num[i]/2;
    for (int i=1;i<=n2;i++)
        for (int j=i+1;j<=n2;j++)
        {
            int x=0;
            for (int k=0;k<m;k++)
                if (s[i][k]==s[j][k]) x++;
            sum[x]+=(ll)num[i]*num[j];
        }
    for (int i=0;i<=m;i++)
        printf("%lld\n",sum[i]);  
    return 0;
}

正解
表示没懂DP

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN=100005;
string str[MAXN],s[MAXN];
ll sum[MAXN],mi3[MAXN],dp[MAXN][12],dp1[MAXN][12];
int n,m,n2,num[MAXN],ss[MAXN];

void solve_small_n()
{
    for (int i=1;i<=n;i++) cin>>str[i];  
    sort(str+1,str+1+n);
    for (int i=1;i<=n;i++)
        if (str[i]!=str[i-1]) 
            s[++n2]=str[i],num[n2]=1;
        else num[n2]++;
    for (int i=1;i<=n2;i++) sum[m]+=(ll)(num[i]-1)*num[i]/2;
    for (int i=1;i<=n2;i++)
        for (int j=i+1;j<=n2;j++)
        {
            int x=0;
            for (int k=0;k<m;k++)
                if (s[i][k]==s[j][k]) x++;
            sum[x]+=(ll)num[i]*num[j];
        }
    for (int i=0;i<=m;i++)
        printf("%lld\n",sum[i]);
}
void solve_small_m()
{
    for (int i = 1; i <= n; i++) {
        int now = 0;
        for (int j = 1; j <= m; j++) {
            now *= 3;
            char k;
            for (k = getchar(); k <= 32; k = getchar());
            now += k - 'x';
        }
        ss[now]++;
    }
    mi3[0] = 1;
    for (int i = 1; i <= m; i++)    mi3[i] = mi3[i - 1] * 3;
    for (int i = 0; i < mi3[m]; i++)    dp1[i][m] = ss[i];
    for (int i = 0; i < m; i++) {
        memcpy(dp, dp1, sizeof dp);
        for (int j = 0; j < mi3[m]; j++) {
            int s1 = j / mi3[i] % 3, s2 = j - s1 * mi3[i];
            for (int t = 0; t < 3 * mi3[i]; t += mi3[i])
                if (t != s1 * mi3[i])
                for (int p = m - i; p <= m; p++)    dp[s2 + t][p - 1] += dp1[j][p];
        }
        memcpy(dp1, dp, sizeof dp);
    }
    for (int i = 0; i < mi3[m]; i++)
        for (int p = 0; p <= m; p++)
            sum[p] += 1LL * ss[i] * dp1[i][p];
    sum[m] = sum[m] - n;
    for (int i = 0; i <= m; i++)    sum[i] /= 2;
    for (int i=0;i<=m;i++)
        printf("%lld\n",sum[i]);    
}
int main()
{
    scanf("%d%d",&n,&m);
    if (m>10) solve_small_n();
    else solve_small_m();
    return 0;
}

T3

问题 C: 分配
时间限制: 1 Sec 内存限制: 128 MB
题目描述
这里写图片描述

输入
这里写图片描述

输出
样例输入
3 0
1
1

3 1
1
1
样例输出
7

0
3
7
提示
这里写图片描述

Solution

CODE

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值