2021山东省省赛补题

本文介绍了两个算法问题,一是利用前后缀匹配寻找平方串,二是优化任务分配以最大化收益。在平方串问题中,通过哈希函数计算字符串的前后缀并计数,找到能拼接成平方串的对数。在任务分配问题中,通过动态规划策略决定人员是作为导师还是学生以获得最大收益。这两个问题都涉及到字符串处理和优化算法的应用。
摘要由CSDN通过智能技术生成

cf传送门

前言

在gym看到,知道山东省很nb所以写了。
补题含大量借鉴大佬代码()

F Birthday Cake

题意:给定n个串,求有多少对串能拼出平方串(能够表示成两个相同的字符串连接在一起的,即AA)。
思路:队友想出来了,就是少考虑了一个情况。
考虑这样一个串:abcdyouareabcd。
前后缀相同。我们可以计数有多少个串youare。
这两个串可以拼接在一起。
还有一种情况就是两个串一模一样。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 4e5+5;
char s[maxn];
typedef long long ll;

// 自然溢出会被卡啊

const int mod1 = 1e9+7;   // 两个模数,再弄个Pair,减少crash
const int mod2 = 998244353;
const int p = 101;

ll base1[maxn],base2[maxn];
ll hs1[maxn],hs2[maxn];

map<pair<ll,ll>,int> mp,mp2;  // 计数菌
// mp用来存整串的hash, mp2用来存去掉相同前后缀后中间部分的hash
// 取子串的hash
pair<ll,ll> subs(int l,int r){
    ll ans1=(hs1[r]+mod1-hs1[l-1]*base1[r-l+1]%mod1)%mod1;
    ll ans2=(hs2[r]+mod2-hs2[l-1]*base2[r-l+1]%mod2)%mod2;
    return make_pair(ans1,ans2);
}

int main(){
    int n;scanf("%d",&n);
    // 初始化处理hash
    base1[0]=1;
    base2[0]=1;
    for(int i=1;i<=maxn;++i){
        base1[i]=base1[i-1]*p%mod1;
        base2[i]=base2[i-1]*p%mod2;
    }
    ll ans = 0;
    while(n--){
        scanf("%s",s+1);
        int len = strlen(s+1);
        hs1[0]=0;hs2[0]=0;
        for(int i=1;i<=len;++i){
            hs1[i] = (hs1[i-1]*p+s[i]-'a'+1)%mod1;
            hs2[i] = (hs2[i-1]*p+s[i]-'a'+1)%mod2;
        }
        pair<ll,ll> o = make_pair(hs1[len],hs2[len]);
        ans += mp[o]; // 加上跟该串一模一样的
        mp[o]++;
        ans += mp2[o]; // 加上内部和{该串整串}一样的串
        for(int i=1;2*i<len;++i){
            // 1 - i, len-i+1 - len
            o = subs(1,i);
            pair<ll,ll> o2 = subs(len-i+1,len);
            if(o==o2){
                // 前后缀一样
                o = subs(i+1,len-i);
                // 中间部分
                ans += mp[o]; // 加上整串和该串内部一样的串
                mp2[o]++;
            }
        }
    }
    printf("%lld\n",ans);
}

J Tuition Agent

题意:n个人,每个人有三个属性,rank,X和Y。要把每个人划分为老师和学生,当老师的rank小于学生的rank,可以将这老师和学生进行配对。配出一对赚k元,将i号变成老师-Xi元,变成学生-Yi元。求最后最多的钱。可能为负。数据随机生成。1e5。
思路:先用R排序(倒序)。
由于老师肯定比学生高级,因此后面放老师相对更优。
d p [ 到 第 几 个 人 ] [ 前 面 有 多 少 个 未 配 对 学 生 ] dp[到第几个人][前面有多少个未配对学生] dp[][]
表示能得到的最多钱。
// step1:做导师
d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i − 1 ] [ j + 1 ] − X i + k ) ; dp[i][j] = max(dp[i][j], dp[i-1][j+1] - Xi + k); dp[i][j]=max(dp[i][j],dp[i1][j+1]Xi+k); // 配对掉一个学生
// step2:做学生
d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i − 1 ] [ j − 1 ] − Y i ) ; dp[i][j] = max(dp[i][j], dp[i-1][j-1] - Yi); dp[i][j]=max(dp[i][j],dp[i1][j1]Yi); // 成为学生
注意到数据随机生成,猜测未配对数较小。
于是暴力跑。
后面数据好像加强了,怎么都过不了了,违背了随机生成的初心(不是)。这道题成为一道无解题。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
typedef long long ll;
const int inf = 1e9;

struct _node{
    int x,y,r;
}node[maxn];

bool cmp(_node a,_node b){
    return a.r<b.r;
}

ll dp[3][3003];

int main(){
    int t;scanf("%d",&t);
    while(t--){
        int n,k;scanf("%d%d",&n,&k);
        for(int i=1;i<=n;++i){
            scanf("%d%d%d",&node[i].r,&node[i].x,&node[i].y);
        }
        // 注意rank小的才能教rank大的
        sort(node+1,node+1+n,cmp);
        int m = min(min(100000000/n+1,n),502);
        for(int i=1;i<=m;++i) dp[0][i]=-inf;
        dp[0][0] = 0;

        for(int i=1;i<=n;++i){
            for(int j=0;j<=m;++j) dp[1][j]=-inf;
            for(int j=0;j<=m;++j){
                // 未配对的有j个
                //                       加入一个tut,未配对的tut就多了1
                if(j<m)
                    dp[1][j+1]=max(dp[1][j+1],dp[0][j]-node[i].x);
                //                   加入一个stu,未配对的tut就减1,配对数+1
                if(j)
                    dp[1][j-1]=max(dp[1][j-1],dp[0][j]-node[i].y+k);
                else
                    dp[1][j]=max(dp[1][j],dp[0][j]-node[i].y);
            }
            for(int j=0;j<=m;++j) dp[0][j]=dp[1][j];
        }
        ll ans = -inf;
        for(int i=0;i<=m;++i)
            ans = max(dp[0][i],ans);
        printf("%lld\n",ans);
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值