2017 CCPC 网络赛 几道水题

博主分享个人训练进度,提及HDU的三道题目。HDU 6152需强行暴力三个点并存边为bool;HDU 6153可使用KMP、EXKMP等算法,博主用EXKMP将后缀问题转化为前缀问题求解;HDU 6156是数位DP形式,设dp状态分三种情况考虑。

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

一次个人训练……果然一个人不是很强啊
进度[3/10] 场上2
HDU 6152 Friend-Graph
垃圾题目卡内存,做法就是强行暴力三个点,然后记得存边存bool

HDU 6153 A Secret
很经典的题目,题意是在S1串中找到S2串每个后缀出现的次数。
首先我们能想到一些求出现次数的算法比如KMP,EXKMP,SA,SAM……
当然这些算法挑一个写就行了。我用的是EXKMP,首先我们把两个串都翻转过来,这样之前求每个后缀的问题就转化成前缀出现的次数。我们直到extend数组有个神奇的性质就是他会把把模板串母串上的匹配情况记录下来,比如extend[i]等于3的时候就相当于长度为3的前缀……但是长度为2的前缀单独出现的时候,不会对长度为3的前缀产生影响。
我们跑一边EXKMP,然后把extend[i]每个数出现的次数记录下,从大到小记录后缀和,每个后缀对应的次数前缀和就是他总共出现的次数,相当于长度大于i的前缀 出现的次数 + 长度为i的字符单独出现的次数。

#include <bits/stdc++.h>
#define ll long long
#define next fuck
using namespace std;
const int MAXN = 1e6 + 10;
const ll mod = 1e9+7;
void pre_EKMP(char x[],int m,int next[])
{
    next[0]=m;
    int j=0;
    while(j+1<m && x[j]==x[j+1])
        j++;
    next[1]=j;
    int k=1;
    for(int i=2; i<m; i++)
    {
        int p=next[k]+k-1;
        int L=next[i-k];
        if(i+L<p+1)
            next[i]=L;
        else
        {
            j=max(0,p-i+1);
            while(i+j<m && x[i+j]==x[j])
                j++;
            next[i]=j;
            k=i;
        }
    }
}
int next[MAXN],extend[MAXN];
int EKMP(char x[],int m,char y[],int n,int next[],int extend[])
{
    pre_EKMP(x,m,next);
    int j=0;
    while(j<n && j<m && x[j]==y[j])
        j++;
    extend[0]=j;
    int k=0;

    for(int i=1; i<n; i++)
    {
        int p=extend[k]+k-1;
        int L=next[i-k];
        if(i+L<p+1)
            extend[i]=L;
        else
        {
            j=max(0,p-i+1);
            while(i+j<n && j<m && y[i+j]==x[j])
                j++;
            extend[i]=j;
            //cout<<j<<endl;
            k=i;
        }
    }
}
char str1[MAXN],str2[MAXN];
ll cnt[MAXN];
int main()
{
    int ca;
    scanf("%d",&ca);
    while(ca--)
    {
        int n,m;
        scanf("%s%s",str1,str2);
        n = strlen(str1);
        m = strlen(str2);
        reverse(str1,str1+n);
        reverse(str2,str2+m);
        pre_EKMP(str2,m,next);
        memset(cnt,0x00,sizeof(cnt));
        memset(next,0,sizeof(next));
        memset(extend,0,sizeof(extend));
        //cout<<str1<<'\n'<<str2<<'\n';
        ll ans = 0;
        EKMP(str2,m,str1,n,next,extend);
        for(int i = 0;i<=n; i++)
        {
            //ans += extend[i];
            //printf("%d ",extend[i]);
            if(extend[i]) cnt[extend[i]]++;
        }
       // puts("");
        cnt[m+1] = 0;
        for(int i = m;i>=0;i--)
        {
            cnt[i] += cnt[i+1];
        }
        for(int i = m;i >= 0;i--)
        {
            ans += 1LL*i*(cnt[i]);
            ans %= mod;
        }
        //puts("");
        printf("%lld\n",ans);
    }
    return 0;
}

HDU 6156 Palindrome Function
一看就是数位DP的形式,但是搞了好久才会做……
我们设dp状态dp[pos][state][len] 代表现在构造到了pos位,现在的数字是不是回文的,回文的长度。
然后分成三种情况考虑,dp下就行

#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll dp[40][101][101][2];
int a[101],tmp[101];
ll dfs(int pos,bool state,int len,bool limit,int k)
{
    if(pos < 0)
        if(state) return k;
        else return 1;
    if(!limit && dp[k][pos][len][state] != -1)
        return dp[k][pos][len][state];

    int up = limit ? a[pos] : k-1;
    ll ans =0;
    for(int i = 0;i<=up;i++)
    {
        tmp[pos] = i;
        if(i == 0 && len == pos)
        {
            ans += dfs(pos-1,state,len-1,limit && (i == up) ,k);
        }
        else if(state && pos < (len + 1) /2)
        {
            ans += dfs(pos-1,i == tmp[len - pos],len,limit && ( i == up),k);
        }
        else
        {
            ans += dfs(pos-1,state,len,limit &&( i == up),k);
        }
    }
    if(!limit) dp[k][pos][len][state] = ans;
    return ans;
}
ll solve(ll n,int k)
{
    int pos = 0;
    while(n)
    {
        a[pos++] = n % k;
        n/=k;
    }
    return dfs(pos-1,1,pos-1,1,k);
}
int main()
{

    int ca,cat = 1;
    scanf("%d",&ca);
    memset(dp,-1,sizeof(dp));
    while(ca--)
    {
        ll L,R,l,r;
        scanf("%I64d%I64d%I64d%I64d",&L,&R,&l,&r);
        ll ans = 0;
        for(int i = l;i<=r;i++)
        {
            ans += solve(R,i) - solve(L-1,i);
        }
        printf("Case #%d: %I64d\n",cat++,ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值