第十八届浙大城市学院程序设计竞赛(同步赛)赛后补题(F、G、I)
比赛传送门
官方题解传送门
F-Palindrome
解题思路:
本题需要我们删除字符串中的两个字符,看是否能够将现有的字符串修改成回文串。
那么对于字符串来说,我们先不考虑删除次数的限制,我们要想把一个字符串变成一个回文串,我们需要的是每次删除首次出现不同的位置,于是我们通过枚举第一个出现不同字符的位置,虽然将其删除后的字符串继续删除首个出现不同的位置。
对于本题来说,我们只需要枚举两次这样的不同位置即可,将操作次数限制到\(2\)次即可。
代码
#include<bits/stdc++.h>
using namespace std;
int k;
string str;
bool dfs(int l,int r,int k)
{
if(l>r) return true;
if(str[l]!=str[r])
{
if(k==0) return false;
else if(k==1) return dfs(l+1,r,k-1)||dfs(l,r-1,k-1);
else return dfs(l+1,r,k-1)||dfs(l,r-1,k-1)||dfs(l+1,r-1,k-2);
}
return dfs(l+1,r-1,k);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--)
{
cin>>k>>str;
if(dfs(0,str.size()-1,2)) puts("Yes");
else puts("No");
}
return 0;
}
G-Permutation
解题思路:
对于本题来说,我们进行操作一和二的先后顺序相互之间是没有影响的,在知道解决办法的情况下,需要进行操作二的时候我们完全可以提前知道该修改哪些值。于是我们可以假设我们先进行操作一,再进行操作二。
假设我们的操作一进行了\(k\)次,那么问题就可以转化成,在\(a\)的后缀\(a[1+k,n]\)和\(b\)的\(b[1,n-k]\)中有多少个位置(相对位置,比如\(a[1+k]\)与\(b[1]\))上的数是不一样的。即我们接下来需要进行操作二的次数、枚举\(k\),问题就可以转化成所有等长的\(a\)后缀和\(b\)前缀相对位置上不同的个数。
这里假设\(a[i]=b[j]=x\),当且仅当相对位置相同,即\(i-(k+1)=j-1\)时,才能让我们少进行一次操作二。本算法的时间复杂度为\(O(N)\)。
代码与解释
第一个\(for\)循环我们记录\(a\)数组中各数值的下标,得到\(pos\)数组,\(cnt[i]\)表示移动\(i\)次后\(a[1+i,n]\)和\(b[1,n-i]\)中有多少个相对位置相同的数,第二个for循环我们去找\(b[i]\)这个值在\(a\)数组中的位置,如果\(pos[b[i]]\)比\(i\)要小,那么说明\(a[pos[b[i]]]\)这个数就没法前移,只有当\(pos[b[i]]\ge i\)的时候,\(a[pos[b[i]]]\)才能前移到当前位置,表示与\(b[i]\)相对位置相同,然后我们通过\(cnt[pos[b[i]]-i]\)的方式表明移动\(pos[b[i]]-i\)次,\(a,b\)中有多少个相对位置相同的数。我们用\(n-i-cnt[i]\)表示\(i+1\sim n\)中需要修改的个数,然后我们再加上移动的次数\(i\),即\(n-i-cnt[i]+i\),化简得到\(n-cnt[i]\),然后取最小值即可。
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int N=1e6+10;
int n;
int a[N],b[N],cnt[N],pos[N];
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
pos[a[i]]=i;
cnt[i]=0;
}
for(int i=0;i<n;i++)
{
scanf("%d",&b[i]);
if(pos[b[i]]>=i)
{
cnt[pos[b[i]]-i]++;
}
}
int res=0;
for(int i=0;i<n;i++)
{
res=max(res,cnt[i]);
}
printf("%d\n",n-res);
return 0;
}
I-String
解题思路:
本题我们需要进行三种操作,分别是
1. 修改\(a\)中某个字符
2. 修改\(a\)中某段区间,使其字典序右移
3. 计算\(a\)和\(b\)的最长公共前缀
由于题目中给出的都是小写的英文字符,因此我们可以将字符串看成是由\(0-25\)的数字组成的数组。我们可以根据这一点,对于\(a\)和\(b\)的差值对\(26\)取模后的值来建立一个线段树,线段树中的每个节点维护区间内不同值的个数。操作\(2\)可以通过对值\(+1\)再对\(26\)取模后得到。如果\(a,b\)的前缀相同,那么节点中差值为\(0\)的个数应该要等于长度,因此我们可以进行树上的二分,找到对应下标即公共前缀的最大长度。本算法的时间复杂度为\(O(N*logN*26)\)
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int M=N*4;//线段树开四倍空间
int n,m;
char a[N],b[N];
int tmp[26];//用来存储移动前的数值数组
struct segtree
{
int cnt[M][26],lz[M];//cnt用来记录当前节点各数值的个数,lz[x]表示当前节点的懒标记
void pushup(int x)
{
for(int i=0;i<26;i++)
cnt[x][i]=cnt[x<<1][i]+cnt[x<<1|1][i];
return;
}
void build(int x,int lx,int rx)
{
if(rx==lx)
{
cnt[x][(a[lx]-b[lx]+26)%26]=1;//取模操作
return;
}
int mid=lx+rx>>1;
build(x<<1,lx,mid);
build(x<<1|1,mid+1,rx);
pushup(x);//用来向上更新
}
//设置懒标记
void setLazy(int x)
{
lz[x]++;
for(int i=0;i<26;i++) tmp[i]=cnt[x][i];
for(int i=0;i<26;i++) cnt[x][(i+1)%26]=tmp[i];
return;
}
void pushdown(int x)
{
if(!lz[x]) return;//如果没有变化就不需要向下更新了
int mov=lz[x];//记下需要右移的次数
lz[x]=0;//记得清空
//对左子树的操作
lz[x<<1]+=mov;
for(int i=0;i<26;i++) tmp[i]=cnt[x<<1][i];
for(int i=0;i<26;i++) cnt[x<<1][(i+mov)%26]=tmp[i];
//对右子树的操作
lz[x<<1|1]+=mov;
for(int i=0;i<26;i++) tmp[i]=cnt[x<<1|1][i];
for(int i=0;i<26;i++) cnt[x<<1|1][(i+mov)%26]=tmp[i];
return;
}
//单点修改
void set(int i,int v,int x,int lx,int rx)
{
if(rx==lx)//到叶节点了
{
for(int i=0;i<26;i++) cnt[x][i]=0;
cnt[x][v]++;
return;
}
pushdown(x);//要先将要经过的节点的懒标记向下传
int mid=lx+rx>>1;
if(i<=mid) set(i,v,x<<1,lx,mid);
else set(i,v,x<<1|1,mid+1,rx);
pushup(x);//最后要向上去更新
}
//区间修改
void modify(int l,int r,int x,int lx,int rx)
{
if(lx>=l&&rx<=r)
{
setLazy(x);//设置一下懒标记
return;
}
pushdown(x);//当然,这里也要先把懒标记向下传
int mid=lx+rx>>1;
if(l<=mid) modify(l, r, x<<1,lx, mid);
if(r>mid) modify(l,r,x<<1|1,mid+1,rx);
pushup(x);
}
//查询操作,树上二分
int query(int x,int lx,int rx)
{
if(rx==lx)
{
if(cnt[x][0]==1) return lx;//说明这个下标的a与b字符相同
else return lx-1;
}
pushdown(x);
int mid=lx+rx>>1;
if(cnt[x<<1][0]==mid-lx+1) return query(x<<1|1, mid+1, rx);
else return query(x<<1,lx,mid);
}
}st;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>a+1>>b+1;
st.build(1,1,n);
while(m--)
{
int op;
cin>>op;
if(op==1)
{
int pos;
char ch[2];
cin>>pos>>ch;
st.set(pos, (ch[0]-b[pos]+26)%26, 1, 1, n);
}
else if(op==2)
{
int l,r;
cin>>l>>r;
st.modify(l, r, 1, 1, n);
}
else
{
cout<<st.query(1, 1, n)<<endl;
}
}
}

浙公网安备 33010602011771号