Codeforces Round #710 (Div. 3)(A~D)(最大公共子串,离散化
这个round做的很差。不知道是因为在学校不习惯还是因为自己又菜了。
比赛时做了3题,第4题一直wa。后来发现忘记判断奇偶性了,第二天起床试了一下就A了。所以勉强算我A了4题吧。
但这次前4题我都没有一发a的。A~D都在test 2上wa过……(太难过了)
接下来就是题解吧:
A.Strange Table
本题题意是对于n行m列的数字矩阵。按行展开时的 按列展开时数字k所在的位置 的值是多少。
因此我们先根据数字k。找到他是第几行第几列。
列的计算为(k-1)/n+1。
行的计算则需要特判几个情况。先计算k%n.如果这个值为0,则令他所在的行数为第n行,如果n=1.则令行数为1.否则行数就为k%n的这个值。
在根据求出的行列位置求出按行展开时该位置上的值即可。(一开始wa就是没想到n=1时的情况。并且把最大行数码成了m)
AC代码如下:
#include<bits/stdc++.h> #define MAXN 100005 using namespace std; typedef long long ll; typedef pair<int,int> pii; int t; ll n,m,x; int main(){ scanf("%d",&t); while(t--){ scanf("%lld%lld%lld",&n,&m,&x); ll c=(x-1)/n+1,r=x%n;//计算第几行和第几列 if(r==0) r=n; if(n==1) r=1; // printf("r:%d,c:%d\n",r,c); ll ans=(r-1)*m+c; printf("%lld\n",ans); } return 0; }
B.Partial Replacement(贪心)
本题的题意是替换*为x。要求是对于第一个和最后一个*都要替换。然后串中任意两个x之间的距离不能大于k。求最少的替换次数。
本题的思路是贪心。
首先先找第一个*和最后一个*。由于题目保证一定有一个*。那么一定存在第一个*。
但由于可能只存在一个*。故不一定存在一个和第一个不同的*。所以如果找不到最后一个*,那么此时令cnt=1输出即可。
如果有多个*。那么将第一个*和最后一个*修改为x后。
从第一个*的位置开始贪心的搜索。从该位置+k的位置从后往前搜索第一个*。将其修改为x并将位置重定位为该位置再令cnt++即可。
需要注意的是如果第一个遍历到的不是*而是x说明最后一个*已经找到了,则直接返回即可。(一开始wa是循环中的continue和break退出和继续的循环没有写清楚……)
AC代码如下:
#include<bits/stdc++.h> #define MAXN 100005 using namespace std; typedef long long ll; typedef pair<int,int> pii; int t; int n,k; char s[55]; int main(){ scanf("%d",&t); while(t--){ memset(s,0,sizeof(s)); scanf("%d%d",&n,&k); scanf("%s",s); int flag=0,pre=0,cnt=0,pos=0;//flag为找第一个*,pre找上一个出现的* ,cnt为答案。 for(int i=0;i<n;++i){ if(s[i]=='*'){ if(!flag){ flag=1; pos=i; ++cnt; s[i]='x'; }else{ pre=i;//如果有第二个*,那么pre肯定不为0 } } } if(pre){//有末尾 ++cnt;//修改次数++ s[pre]='x';//将最后一个*修改为x }else{//无末尾,即只有一个* printf("1\n"); continue;; } while(pos+k<n){ flag=0; for(int i=pos+k;i>pos;--i){ // printf("i:%d\n",i); if(s[i]=='x'){//如果后面的数已经有x了,说明最后一个数在范围内 flag=0; break; }else if(s[i]=='*'){//如果第一个出现的是*。表明后面还有*或者x s[i]='x';//修改这个*为x ++cnt;//计数++ pos=i;//将倒数第二个x的地址改为这个点 // printf("pos:%d\n",pos); flag=1; break; } } if(!flag) break; } printf("%d\n",cnt); } return 0; }
C.Double-ended Strings.(最大公共子串)
题意是a,b两个字符串,可以分别删除串首和串尾的若干个字符。问最少删几个能令两个字符串相等。(如果两个字符串都为空则也相等)
本题就是求a,b两个字符串的最大公共子串。且我们注意到两个子串的长度都不大,只有20.(所以也可以直接暴力匹配)
但是我突然发现我不会求两个字符串的最大公共子串orz……于是赶快上网抄了个模板。但一开始抄的模板有点问题……就wa了。
换了个模板抄就完事了。
现在我们来仔细看看如何求两个字符串的最大公共子串。(如果数据比较大好像要后缀自动机……)
AC代码如下:
#include <iostream> #include <vector> #include<algorithm> #include<string> using namespace std; int lenSIZE(string a, string b) { //初始化a.size()*b.size()的二维数组,其数组元初始化为0 vector<vector<int>> num(a.size()); vector<int> temp(b.size(), 0); for (int i = 0; i < num.size(); i++) { num[i].resize(b.size());//开列数 num[i]=temp; } int len = 0; for (int i = 0; i < a.size(); i++) {//行 for (int j = 0; j < b.size(); j++) { if (a[i] == b[j]) { if (i > 0 && j > 0) { num[i][j] = num[i - 1][j - 1] + 1; len=max(len, num[i][j]); } else { num[i][j] = 1; len = max(len, num[i][j]); } } } } return len; } int main(int argc, char** argv) { int t; scanf("%d",&t); while(t--){ string a,b; cin>>a>>b; int len = lenSIZE(a, b); cout<<(a.size()+b.size()-2*len)<<endl; } return 0; }
D. Epic Transformation(离散化)
本题是给你一个数列。可以任意删去一对不相同的数。求最后最少可以剩几个。
我们可以通过玄学分析发现。当一个数的重复次数不超过n/2(n为数列长度)时,一定可以全部消掉。
同时由于a【i】的数据有点大。但一共也就2e5个数,因此我们可以对其进行离散化优化。
但这时还需要考虑n的奇偶性。当n为偶数时则全部消掉输出0,当n为奇数时则显然会剩下一个(因为是两个两个消的)。故输出1即可。
如果一个数的重复次数超过n/2.则n减去除该数以外的数的出现次数的数对(即*2)再输出即可。
AC代码如下:
#include <iostream> #include <vector> #include<algorithm> #include<cstring> #define MAXN 200005 using namespace std; int a[MAXN],vis[MAXN]; int main(){ int t; scanf("%d",&t); while(t--){ int n; memset(vis,0,sizeof(vis)); scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&a[i]); } sort(a,a+n); int cnt=0; vis[0]=1; int maxn=0; for(int i=1;i<n;i++){ if(a[i]==a[i-1]) vis[cnt]++; else{ if(vis[cnt]>maxn) maxn=vis[cnt]; vis[++cnt]++; } }//0~cnt离散化。k if(vis[cnt]>maxn) maxn=vis[cnt]; if(2*maxn>n) printf("%d\n",n-2*(n-maxn)); else{ if(n&1) printf("1\n"); else printf("0\n"); } } }