#CF Educational Codeforces Round 134 (Rated for Div. 2) A-E复盘

1.a题 模拟

思路:

分四种情况进行考虑即可

2.b题 贪心

题意:

一个n*m网格,某个位置有一格“黑洞”,所有和黑洞的曼哈顿距离小于等于s的格子都不能踩否则会被吸进去,求从左上角到右下角的最短步数,若不能输出-1

思路:

题目只保证起点不在黑洞范围,故先判断终点是否在黑洞范围中。注意到只要不走回头路,到达终点步数就一样,不难发现沿着网格边缘到终点的两条路(左上、右下)一定是最优解,假如这两条路都被阻断,其他的任意一条路径都不可能到达终点。

证明:

可以想象一个以黑洞为中心的十字架,若两条最优路径都不行,十字架必然会把起点与终点完全隔开。

完整代码

#include <bits/stdc++.h>
 
#define pb push_back
#define fi first
#define se second
#define makp make_pair
 
using namespace std;
 
typedef pair<int, int> PII;
typedef long long LL;
const int Mod=1e9+7,N=2e5+10;
 
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}
 
void acc()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
}
 
int n,m,sx,sy,d;
 
// 1111:3
// 211:2
// 31:1
// 4:0
 
int sol()
{
    if(abs(sx-n)+abs(sy-m)<=d)return -1;
    if(sx-d>1&&sy+d<m)return n+m-2;
    if(sx+d<n&&sy-d>1)return n+m-2;
    return -1;
}
int main()
{
    int t;
    cin>>t;
    while(t--){
        cin>>n>>m>>sx>>sy>>d;
        cout<<sol()<<endl;
    }
}

3.c题 贪心

题意:

数组a(单调不减)每个数加上一个非负增量再正序排列后得到数组b(单调不减),现在给你数组a、b,依次询问你增量数组d的每个元素的最大、最小可能值(每个询问间相互独立)。

思路:

注意到a数组中的下标为i的元素加上d[i]后对应到b数组的元素下标j未必满足i==j,那么我们的任务就是找到某种对应方式使得d[i]最大或最小。
发现让a、b数组对应元素下标相等,是一定符合题意的,这一点用反证法即可。
求d[i]最小可能值:对于每个下标i,找d[i]min就是找到bi数组第一个大于等于a[i]的元素下标j,并且让b[j]与a[i]对应。可以证明,这样是一定可行的。
求d[i]的最大可能值:我们可不可以直接找到b数组最大的元素让其与a[i]对应,这样d[i]max=b[j]max-a[i]?答案是不可以。如a=[1 2 3],b=[1 2 4] 。这是因为,如果让a[i]和b[j]对应,那么i到j之间的元素a[k]的最优解就是集体向前错位,与b[k-1]对应(如下图所示),而我们知道,b[k-1]可能小于a[k],这与d[k]>=0矛盾。所以我们要二分得到最大的j,使得i到j之间的元素都满足b[k-1]>=a[k]。这可以用前缀和进行优化。
在这里插入图片描述

完整代码

#include <bits/stdc++.h>
 
#define pb push_back
#define fi first
#define se second
#define makp make_pair
 
using namespace std;
 
typedef pair<int, int> PII;
typedef long long LL;
const int Mod=1e9+7,N=2e5+10;
 
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}
 
void acc()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
}
 
int n,a[N],b[N],sum[N];
 
bool check(int x0,int x1){
    return (sum[x1]-sum[x0]==x1-x0);
}
 
int main()
{
    int t;
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=1;i<=n;i++)cin>>a[i];
        for(int i=1;i<=n;i++)cin>>b[i];
        for(int i=2;i<=n;i++){
            if(a[i]<=b[i-1])sum[i]=sum[i-1]+1;//可补位
            else sum[i]=sum[i-1];
        }
        for(int i=1;i<=n;i++){
            cout<<b[lower_bound(b+1,b+1+n,a[i])-b]-a[i]<<" ";
        }
        cout<<endl;
        for(int i=1;i<=n;i++){
            int l=i,r=n;
            while(l<r){
                int mid=l+r+1>>1;
                if(check(i,mid))l=mid;
                else r=mid-1;
            }
            cout<<b[l]-a[i]<<" ";
        }
        cout<<endl;
    }
}

4.d题 贪心

题意:

已知数组a、b,而数组c满足c[i] = a[i] ^ b[i] (^是异或)。让你适当排列数组b,求得到的数组c所有元素相与(&)的最大值。

思路:

设数组c所有元素相与(&)的结果为res,不妨考虑某一位,假设res第j位为1,说明对任意i,a[i]、b[i]恰有一个第j位为0,一个第j位为1,等价于a数组第j位为1的个数=b数组第j位为0的个数,数学表示:a&(2^j) ^ b&(2^j)=1 (式子1)
为了让res更大,我们贪心地从高位到低位枚举,看每一位是否满足a数组该位为1的个数=b数组该位为0的个数,若满足,则将a数组的1与b数组的0分在一组,a数组的0与b数组的1分在一组。以此类推。
然而需要注意的是,本题中每一位之间其实并不独立,当我们针对某一位进行分组时,会对每一位进行同样的分组操作。
一种解法是模拟,每次分组,相当于产生了一个新组,只需要用变量维护组数信息即可;利用map,可以把同一组的元素集合在一起,进而判断该位是否满足。这种做法常数较大。
另一种解法稍微转了个弯。设截止到第j位时,之前所有满足的位为[k1,k2,…,km],发现第j位满足,等价于对任意的i,a[i],b[i]的[k1,k2,…,km,j]这些位相反。数学表示为:a[i]&binary(k1,k2,…,km,j) ^ b[i]&binary(k1,k2,…,km,j) = binary(k1,k2,…,km,j) ,发现这个式子与上面的式子1类似,因此,我们只需对a,b数组按a[i]&binary(k1,k2,…,km,j)分组,对于某一组判断其是否与对应组元素相等即可。

解法1:完整代码(较麻烦)

#include <bits/stdc++.h>
 
#define pb push_back
#define fi first
#define se second
#define makp make_pair
 
using namespace std;
 
typedef pair<int, int> PII;
typedef long long LL;
const int Mod=1e9+7,N=2e5+10;
 
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}
 
void acc()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
}
 
int n,a[N],b[N],cnta[35][N],cntb[35][N],gra[N],grb[N],idx,power[35];
 
int main()
{
    acc();
    int t;
    cin>>t;
    //
    int pp=1;
    for(int j=0;j<=30;j++){
        power[j]=pp;
        pp<<=1;
    }
    while(t--)
    {
        cin>>n;
        for(int i=1;i<=n;i++)cin>>a[i];
        for(int i=1;i<=n;i++)cin>>b[i];
        //ini
        int ans=0;
        for(int i=1;i<=n;i++)
            for(int j=0;j<30;j++)cnta[j][i]=cntb[j][i]=0;
        for(int i=1;i<=n;i++)gra[i]=grb[i]=0;
        idx=0;
        
        //process
        for(int i=1;i<=n;i++){
            int x=a[i];
            for(int j=0;j<30;j++){
                if(x&1)cnta[j][i]++;
                x>>=1;
            }
            
            x=b[i];
            for(int j=0;j<30;j++){
                if(x&1)cntb[j][i]++;
                x>>=1;
            }
        }
        
 
        //solve
        for(int k=29;k>=0;k--){
            map<int,vector<int>>mpa,mpb;
            for(int i=1;i<=n;i++){
                mpa[gra[i]].pb(i);
                mpb[grb[i]].pb(i);
            }
            //统计是否每组都满足cnta+cntb=组中元素个数
            bool flag=1;
            for(auto i:mpa)
            {
                int grid=i.fi;
                vector<int> va=i.se;
                vector<int> vb=mpb[grid];
                int cnt=0;
                for(int j:va)cnt+=cnta[k][j];
                for(int j:vb)cnt+=cntb[k][j];
                if(cnt!=va.size()){
                    flag=0;
                    break;
                }
            }
            if(!flag)continue;//假如存在某组不满足,则该位不可以
            
            ans+=power[k];//维护答案
            //分组
            for(auto i:mpa)
            {
                int grid=i.fi;
                vector<int> va=i.se;
                vector<int> vb=mpb[grid];
                if(va.size()+vb.size()<=2)continue;//人数不够,不分组
                idx++;
                for(int j:va)
                    if(cnta[k][j])gra[j]=idx;
                for(int j:vb)
                    if(!cntb[k][j])grb[j]=idx;
                
            }
        }
        
        cout<<ans<<"\n";
    }
    return 0;
}

解法2:完整代码

#include <bits/stdc++.h>
 
#define pb push_back
#define fi first
#define se second
#define makp make_pair
 
using namespace std;
 
typedef pair<int, int> PII;
typedef long long LL;
const int Mod=1e9+7,N=2e5+10;
 
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}
 
void acc()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
}
 
int n,a[N],b[N],power[35];
 
int main()
{
    int t;
    cin>>t;
    //
    int pp=1;
    for(int j=0;j<=30;j++){
        power[j]=pp;
        pp<<=1;
    }
    while(t--)
    {
        cin>>n;
        for(int i=1;i<=n;i++)cin>>a[i];
        for(int i=1;i<=n;i++)cin>>b[i];
        //ini
        int ans=0;
        
       
        for(int j=29;j>=0;j--){
            int res=ans|power[j];
            bool flag=1;
            map<int,int> mpa,mpb;
            for(int i=1;i<=n;i++)mpa[a[i]&res]++;
            for(int i=1;i<=n;i++)mpb[b[i]&res]++;
            for(auto i:mpa){
                int x=i.fi,cnt=i.se;
                int cnt1=mpb[x^res];
                if(cnt!=cnt1){
                    flag=0;
                    break;
                }
            }
            if(flag)ans=res;
        }
        cout<<ans<<"\n";
        //a ++ b = c
        //a++c=a++a++b=0++b=b
        
    }
    return 0;
}
 

5.e题 KMP+优化

题意:

给你一个模式串s,现在有多次询问,每次给你一个新串s’,求将其拼在s后面,其每个下标的最大公共前后缀。

思路:

最大公共前后缀必然想到kmp,我们考虑对模式串s用kmp进行预处理,然后每次询问时,对于新拼接的串,从拼接处向后执行kmp。根据kmp算法的意义,这样得到的结果就是拼接串的最大公共前后缀。
但很不幸,这样是不够的,虽然新串很短,但模式串s很长,一次询问的最坏复杂度达到了惊人的O(n+m)。这是为啥呢?发现问题主要是 while(j&&s[i-1]!=s[j])j=ne[j],如果一开始的j很大,j减小的步幅很小,就会使得复杂度趋于n+m;如果我们能让这个循环变为O(1),那么一次询问的复杂度只有O(m),m很小。
具体做法是:维护一个数组negood[i][j],表示待匹配位下标为j,字母为i时,上述循环执行到终点时j的新下标j’,这样就可以让循环一步到位。维护negood数组的方法就是从前往后递推的思想,类似于dp
实现的时候注意由于模式串和拼接串要维护2个ne数组,所以要对原kmp算法做一些小修改,如下文的get_s和funcne函数

完整代码

#include <bits/stdc++.h>
 
#define pb push_back
#define fi first
#define se second
#define makp make_pair
 
using namespace std;
 
typedef pair<int, int> PII;
typedef long long LL;
const int Mod=1e9+7,N=1e6+100;
 
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}
 
int n,ne[N],m,negood[30][N];
bool st[N];
 
string s;
 
void acc()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
}
 
char get_s(int x,string s1)
{
    if(x<n)
    {
        return s[x];
    }
    else return s1[x-n];
}
 
int funcne(int x,int y)
{
    if(x<n){
        //cout<<x<<" "<<y<<" "<<negood[y][x]<<endl;
        return negood[y][x];
    }
    else return ne[x];
}
 
 
int main()
{
    //考虑预处理26个英文字母
    cin>>s;
    n=s.length();
    //求ne函数
    for(int i=2,j=0;i<=n;i++)
    {
        while(j&&s[i-1]!=s[j])j=ne[j];//匹配不了,就后退,直到匹配成功或无路可退
        if(s[j]==s[i-1])j++;//匹配了,进度++
        ne[i]=j;//记录最大公共前后缀,即ne[i]
    }
    //求negood,一步到位
    for(int i=2;i<=n;i++){
        int tmp=i;
        tmp=ne[tmp];
        negood[s[tmp]-'a'][i]=tmp;
        for(int j=0;j<26;j++)negood[j][i]=max(negood[j][i],negood[j][tmp]);
    }
    int t;
    cin>>t;
    
    while(t--)
    {
        string s1;
        cin>>s1;
        m=s1.length();
        int ans[15];
    
    for(int j=ne[n],i=1;i<=m;i++)
    {
        while(j&&s1[i-1]!=get_s(j,s1))j=funcne(j,s1[i-1]-'a');//匹配不了,就后退,直到匹配成功或无路可退
        if(s1[i-1]==get_s(j,s1))
        {
            //cout<<j<<" "<<s1[i-1]<<" "<<get_s(j,s1)<<endl;
            j++;//匹配了,进度++ 
        }
        
        ne[i+n]=j;//记录答案
    }
        for(int i=1;i<=m;i++)cout<<ne[i+n]<<" ";
        cout<<"\n";
        
    }
    return 0;
    
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值