2023牛客多校4(A,L,F)

A题题解
前导知识:KMP算法(c++):https://blog.csdn.net/crazy_scott/article/details/79211961
解题思路:
给的一个t,要求规定长度n的s,要求组合成的字符串只出现两次t
所以在s内部不能有,在s和t衔接的部分不能有t
t我们分为两种:
1.只有1或者0,这样的话我们让s等于全1或全0就行
2.01都有,这样的话我们的s在内部不能有t,s可以为全1或者全0。
所以我们可以知道全零或全一是一定成立的。
接下来使用KMP匹配t出现的次数就可以了。
枚举两种情况如果只出现两次就成立。

#include<iostream>
using namespace std;
int* getNext(string p)//获取下一步的坐标
{
    int* next = new int[p.length()];//动态整形数组
    next[0] = -1;
    int j = 0;
    int k = -1;
    while (j < (int)p.length() - 1)
    {
        if (k == -1 || p[j] == p[k])
        {
            j++;
            k++;
            next[j] = k;
        }
        else
        {
            k = next[k];
        }
    }
    return next;
}
int KMP(string T, string p)
{
    int i = 0, ans = 0;
    int j = 0;//初始化i,j从零开始搜索
    int* next = getNext(T);
    while (i < (int)T.length())
    {
        if (j == -1 || T[i] == p[j])
        {
            i++;
            j++;
        }
        else
        {
            j = next[j];
        }
        if (j == (int)p.length())
        {
            j = next[j];//这步非常关键,让j等于最长公共后缀的下标
            ans++;
        }
    }
    return ans;
}
int main() {
    int T, n; std::string t, ans;
    std::cin >> T;
    while (T--) {
        std::cin >> n >> t;
        string s1, s0;
        for (int i = 1; i <= n; i++) {
            s1 += "1";//初始化两个全一和全零数组,用来比较
            s0 += "0";
        }
        if (KMP(t + s0 + t, t) == 2) {
            std::cout << s0 << "\n";
        }
        else if (KMP(t + s1 + t, t) == 2) {
            std::cout << s1 << "\n";
        }
        else {
            std::cout << "-1\n";
        }
    }
    return 0;
}

F题题解
这类顺序问题要使用到https://blog.csdn.net/qq_42185999/article/details/115352951#//pair
要使用pair进行排序,可以快速查询顺序。
对数据排序,可以发现,每回排除的都是极左和极右的人,所以只要判断中间的人是倾向左边还是右边的。
奇数的时候是中间的人,偶数的时候是中间偏左的人,因为中间的投右边肯定他的右边都投右边。

#include<iostream>
#include <utility>
#include<algorithm>
typedef long long ll;
const int N=1e6+1;
std::pair<int,int>x[N];
int main(){
    int n,y,z,l,r,m,q;
    std::cin>>n;
    for(int i=1;i<=n;i++){
        std::cin>>y;
        x[i].first=y;//第一个是价值
        x[i].second=i;//第二个是顺序
    }
    sort(x+1,x+n+1);
    l=1,r=n;
    while(l<r){
        m=(l+r)/2;
        if(x[r].first-x[m].first>=x[m].first-x[l].first){//这里不分奇数与偶数,因为m是向下取整
            r--;//只要满足右边边和中间的距离大于等于左边到中间距离即可
        }
        else l++;
    }
    std::cout<<x[r].second;
    return 0;
}

L题题解
这题因为前面的操作会覆盖后面的操作,所以我们从后面到前面开始计数。
每一行数据我们都用数组记录。并且两个布尔数组记录这一行或一列是否操作过。
对于开灯的情况,分为行操作和列操作,行时加上列数减去已操作的列数,同时行操作数加一。
对于关灯时,我们不加也不减去开灯数,只是分别加上对应的操作数。

#include<iostream>
#include <utility>
#include<algorithm>
typedef long long ll;
const int N = 1e6 + 10;
bool r[N], c[N];
int main() {
    ll m, n, q, a[N]; std::string s[N], s1[N];
    std::cin >> n >> m >> q;
    for (int i = 1; i <= q; i++) {
        std::cin >> s[i] >> a[i] >> s1[i];
    }
    ll ans = 0, cnt = 0, cnt1 = 0;
    for (int i = q; i >= 1; i--) {
        if (s1[i] == "on") {
            if (s[i] == "row" && !r[a[i]]) {
                ans += m - cnt1; r[a[i]] = 1;//已经加过的记录一下
                cnt++;//记录已经加了几行
            }
            else if (s[i] == "column") {
                if (!c[a[i]]) {
                    ans += n - cnt; cnt1++;
                    c[a[i]] = 1;
                }
            }
        }
        else {
            if (s[i] == "row" && !r[a[i]]) {
                r[a[i]] = 1; cnt++;
            }
            else if (s[i] == "column") {
                if (!c[a[i]]) {
                    c[a[i]] = 1;
                    cnt1++;
                }
            }
        }
    }
    std::cout << ans;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值