【牛客OI周赛7-普及组 非官方题解】暴力,二分,KMP,尺取(STL或Hash)

版权声明:欢迎学习我的博客,希望ACM的发展越来越好~ https://blog.csdn.net/qq_41289920/article/details/87900042

A:

链接:https://ac.nowcoder.com/acm/contest/372/A
来源:牛客网
 

某天,一只可爱的肥橘喵在路上走,突然遇到了一个怪人,那怪人自称PM6,“小肥喵,这里有一道水题,答对了我就请你吃狗肉,答错了你就请我吃猫肉!”

喵咪瑟瑟发抖:“QAQ什么题?”

PM6道:“给你坐标轴上的N个点,求出对于每个点,有多少个点的 X 坐标和 Y 坐标都大于它。”

毫不意外,蠢肥喵完全不会这道题并面临着被做成猫肉火锅的危险,求求你救救喵咪!

输入描述:

输入包括两行,第一行是正整数n,表示点数,接下来N行每行两个数表示第i个点的横坐标和纵坐标,坐标值都是整数,输入数据中存在坐标相同的点。
对于50%的数据:0<=点的坐标大小<=10000,0<=N<=100
对于100%的数据:0<=点的坐标大小<=10000,0<=N<=1000

输出描述:

输出包括N行,第i行表示有多少个点在点i的右上方。

示例1

输入

复制

3
1 2
2 3
4 4

输出

复制

2
1
0

解题报告:

   暴力没啥好说的。

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e5 + 5;
pair<int,int> p[MAX];
int main()
{
    int n;
    cin>>n;
    for(int i = 1; i<=n; i++) {
        scanf("%d%d",&p[i].first,&p[i].second);
    }
    for(int i = 1; i<=n; i++) {
        int ans = 0;
        for(int j = 1; j<=n; j++) {
            if(p[j].first > p[i].first && p[j].second > p[i].second) ans++;
        }
        printf("%d\n",ans);
    }
 
    return 0 ;

B:

题干:

链接:https://ac.nowcoder.com/acm/contest/372/B
来源:牛客网
 

某天,一只可爱的小兔砸在路上蹦蹦跳跳地走着,怪人PM6出现了,于是小兔子被盯上了。

PM6:“免子。哦不,小兔子。你长得真好…不对,真可爱。我这里有一道很容易很容易的题目,答对了我就请你吃萝卜,答错了你就请我吃兔肉,好不好呀~~?”

小兔砸:“萝卜!?好呀好呀好呀。”于是笨笨的兔纸入套了。

PM6:“我这里有一个由 N 个数组成的序列,给你 M 个询问,每个询问会给你一个数 X ,对于每个询问,你要回答出序列中与这个值最接近的元素。”

听完题后,兔子吓成一坨免子了,面临着变成红烧兔头的危险,求求你救救兔子!

输入描述:

第一行包含一个整数N,为序列长度。
第二行包含N个整数,为序列各元素。
第三行包含一个整数M,为PM6的询问个数。
接下来M行,每行一个整数X,为要询问最接近元素的给定值。
对于40%的数据:1<=N<=10000,1<=M<=1000
对于另外10%的数据:M=1
对于100%的数据:1 <=N<= 100000,1<=M<=10000,0<=序列中的每个数,X<=1e9

输出描述:

M行,每行有一个整数,为最接近相应给定值的元素值,保持输入顺序。若有多个值满足条件,输出最小的一个。

示例1

输入

复制

5
2 4 5 5 7
3
2
5
6

输出

复制

2
5
5

解题报告:

   二分就好了,注意特判第一个数和最后一个数,因为这两个不能用下面那个通式(因为可能pos-1==0 或者pos==n+1,此时不能放到数组中取对应数(因为都是0))(貌似很多人因为没有特判所以80分了这题。。)

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e5 + 5;
int a[MAX];
int main()
{
    int n,m;
    cin>>n;
    for(int i = 1; i<=n; i++) scanf("%d",a+i);
    sort(a+1,a+n+1);
    cin>>m;
    while(m--) {
        int x;
        scanf("%d",&x);
        int pos = lower_bound(a+1,a+n+1,x) - a;
        if(pos == 1) {
            printf("%d\n",a[1]);continue;
        }
        if(pos == n+1) {
            printf("%d\n",a[n]);continue;
        }
        if(abs(a[pos] - x) >= abs(a[pos-1]-x)) printf("%d\n",a[pos-1]);
        else printf("%d\n",a[pos]);
         
    }
    return 0 ;
 }

 C:

题干:

链接:https://ac.nowcoder.com/acm/contest/372/C
来源:牛客网
 

另一天,一只可爱的围着围巾的肥企鹅在路上摇摇晃晃地走着,遇上了迎面走来的打着饱嗝的PM6。小企鹅预感不妙,这不就是最近有名的恶人PM6么!吓得立刻扭头就想跑。

PM6:“小火汁,站住!我不吃你(谁叫你是保护动物)。我这有一道简单题,如果你答对了,我就给你吃鱼肉,如果你答错了,就免费帮我充游戏币!”

企鹅:“_(:3J∠)_(默默摘掉围巾)”

PM6:“我给你一个文本串 S ,再给你两个串A、B,你要将文本串中的 A 都转换成 B ,转换后的字符不再参与转换,输出最终的文本串。”

求求你救救企鹅!

输入描述:

第一行输入一个文本串 S 。
第二行输入字符串 A 。
第三行输入字符串 B 。
|S|为S的长度,|A|为A的长度,|B|为B的长度,所有字符都是小写字母,保证 |A| <= |S| 。
对于50%的数据:1<= |A|、|B|、|S| <=1000
对于100%的数据:1<= |A|、|B|、|S| <=1000000

输出描述:

只有一行,输出转换后的文本串。

示例1

输入

复制

abababcd 
ab
cd

输出

复制

cdcdcdcd

解题报告:

    KMP就好了,记录下来每一个匹配的第一个位置,然后遍历整个字符串,遇到匹配的位置的时候就输出替换串,直到遍历完第一个字符串。

AC代码:

#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
char s[1000005];
char t[1000005];
char ac[1000005];
int Next[1000005];
int ans[1000005];
int len1,len2,cnt;
void getnext() {
    int j = 0,k = -1;
    Next[0] = -1;
    while(j<len2-1) {
        if(k == -1 || t[j] == t[k]) {
            j++,k++;
            Next[j] = k;
        } else k = Next[k];
    }
}
int kmp() {
    int i=0,j=0;
    while(i < len1) {
        if(j == -1 || s[i] == t[j]) {
            i++,j++;
        } else {
            j=Next[j];
        }
        if(j >= len2) {
            ans[++cnt] = i-len2;
            j=0;
        }
    }
    return cnt;
  
}
int main() {
  
    scanf("%s",s);
    scanf("%s",t);
    scanf("%s",ac);
    len1 = strlen(s);
    len2 = strlen(t);
    getnext();
    kmp();
    //printf("%d\n",kmp());
    int cur = 1;
    int i = 0;
    //cout << "ans**" << ans[1]<<endl;
    while(1) {
        if(i >= len1) break;
        if(cur <= cnt) {
            while(i < ans[cur]) {
                printf("%c",s[i]);i++;
            }
            cur++;
            i += len2;
            printf("%s",ac);
        }
        else {
            printf("%c",s[i]),i++;
        }  
    }
    return 0;
}

题干:

链接:https://ac.nowcoder.com/acm/contest/372/D
来源:牛客网
 

可能很多人要吐槽为什么标题不是“救救blabla”了。

怪人PM6喜欢数糖纸,不同的糖纸有不同的颜色,一共有 N 张糖纸,第 i 张糖纸颜色为 Ci ,它们的位置都是固定的。PM6喜欢五彩缤纷的糖纸,所以他不希望有重复的颜色。他有一次机会,可以收集任意一段连续区间内的糖纸。求出PM6最多能收集多少张糖纸。

输入描述:

第一行一个正整数 N ,表示共有 N 张糖纸。
第二行共有 N 个正整数,第 i 个正整数表示第 i 张糖纸的颜色 Ci
对于20%的数据:1<=N<=100
对于40%的数据:1<=N<=1000
对于100%的数据:1<=N<=1e6,0<=Ci<=1e9

输出描述:

一个整数表示PM6最多能收集多少张糖纸。

示例1

输入

复制

5
1 2 2 3 4

输出

复制

3

说明

PM6可以收集第3到第5张的糖纸,共有三张。

解题报告:

     尺取法。

AC代码:(每次固定右边界,来移动左边界)

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
#define maxn 1000000
int a[maxn+5];
bool b[1000000005];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    int l=0,ans=1;
    for(int i=1;i<=n;i++){
        while(b[a[i]])
            b[a[++l]]=false;
        b[a[i]]=true;
        ans=max(ans,i-l);
    }
    printf("%d\n",ans);
    return 0;
}

AC代码2:(固定左边界看可以到达的右边界)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e6 + 5;
const int FFF = 5e7 + 6; 
int mp[MAX];
int cnt,a[MAX];
bool vis[FFF];
int HH(int x) {
	return (1LL*13531*x)%(FFF-100);
}
int main()
{
    int n;
    cin>>n;
    for(int i = 1; i<=n; i++) scanf("%d",&a[i]),mp[i] = HH(a[i]);
    int l = 1,r = 1,ans = 0;
    while(l<=r && l <= n) {
         
        while(vis[mp[r]] == 0 && r <= n) {
            vis[mp[r]] = 1,r++;
        }
        ans = max(ans,r-l);
        vis[mp[l]] = 0;
        l++;
    }
    printf("%d\n",ans);
    return 0 ;
 }

这个实现方法很多,可以Hash可以直接开1e9的数组可以unordered_map,可以用二分来离散化(但是离散化完了要重新记录到一个数组中,这样使用的时候是O1的,不能每次使用都从重新计算,因为常数有点扛不住、、)

并且Hash的时候注意不能用rand(),不然可能本来相同的值可能就会被映射到不同地方去了。

这样写显然错误:(越界了)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e6 + 5;
const int FFF = 5e7 + 6; 
int mp[MAX];
int cnt,a[MAX];
bool vis[FFF];
int HH(int x) {
	return (1LL*13531*x)%(FFF-1);
}
int main()
{
    int n;
    cin>>n;
    for(int i = 1; i<=n; i++) scanf("%d",&a[i]),mp[a[i]] = HH(a[i]);
    int l = 1,r = 1,ans = 0;
    while(l<=r && l <= n) {
         
        while(vis[mp[a[r]]] == 0 && r <= n) {
            vis[mp[a[r]]] = 1,r++;
        }
        ans = max(ans,r-l);
        vis[mp[a[l]]] = 0;
        l++;
    }
    printf("%d\n",ans);
    return 0 ;
 }

常数很小的unordered_map(400ms)(我写的就800ms、、)

#include<cstdio>
#include<unordered_map>
using namespace std;
 
int main()
{
    unordered_map<int,int>pos;
    int n,x,now=0,ans=1;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        now=i-pos[x]>now?now+1:i-pos[x];
        ans=ans>now?ans:now;
        pos[x]=i;
    }
    printf("%d\n",ans);
    return 0;
}

还可以直接取模:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
#define N 1000001
#define P 1651469
int n,a[N],ans=0;
bool v[N*2];
int main() {
	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	int l=1,r=1;
	v[a[1]%P]=1;
	while(r<n) {
		if(v[a[r+1]%P]) {
			while(a[r+1]!=a[l])v[a[l++]%P]=0;
			l++;
		}
		v[a[++r]%P]=1;
		ans=max(ans,r-l+1);
	}
	cout<<ans<<endl;
}

写在后面:

   纪念第一场ak...虽然题目很水但毕竟是不熟悉的OI赛制。继续加油!

猜你喜欢

转载自blog.csdn.net/qq_41289920/article/details/87900042