正睿 CSP 七连测 day3

T1

水题,直接贪心的去做就可以。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N number
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int a[10],n=7;

int main(){
    for(int i=1;i<=n;i++) read(a[i]);
    int ans=0;
    for(int i=1;i<=n;i++) ans+=i*a[i];
    int w=-1;
    for(int i=n;i>=1;i--){w=i;if(a[i]) break;}
    if(w==1) w--;ans+=w*a[1];
    printf("%d\n",ans);
    return 0;
}

T2

不难发现,如果第一行哪些格子按决定了,那么剩下的就都决定了,直接暴力枚举即可。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 200010
#define M 20
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

template<typename T> inline T Max(T a,T b){return a<b?b:a;}
template<typename T> inline T Min(T a,T b){return a<b?a:b;}

int a[N],tail,n,m,b[M][M],c[M][M],ans=INF;
char s[M];

inline void dfs(int w,int now){
    if(w==m){a[++tail]=now;return;}
    dfs(w+1,now<<1);
    dfs(w+1,now<<1|1);
}

const int fx[]={0,0,0,1,-1};
const int fy[]={0,1,-1,0,0};

inline void Change(int x,int y){
    b[x][y]^=1;
    for(int i=1;i<=4;i++){
        int dx=x+fx[i],dy=y+fy[i];
        if(dx<=0||dy<=0||dx>n||dy>m) continue;
        b[dx][dy]^=1;
    }
}

// #define fre
// #define debug

int main(){
    #ifdef fre
        freopen("my.in","r",stdin);
        freopen("my.out","w",stdout);
    #endif
    read(n);read(m);
    dfs(0,0);
    #ifdef debug
        tail=0;a[++tail]=17;
    #endif
    for(int i=1;i<=n;i++){
        scanf("%s",s+1);
        for(int j=1;j<=m;j++){
            if(s[j]=='X') b[i][j]=1;else b[i][j]=0;
            c[i][j]=b[i][j];
        }
    }
    for(int i=1;i<=tail;i++){
        for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) b[i][j]=c[i][j];
        int nowans=0;
        for(int j=0;j<=m-1;j++){
            if((a[i]>>j)&1){
                // printf("i:%d j:%d\n",i,j+1);
                Change(1,j+1);nowans++;
            }
        }
        bool pd=1;
        for(int i=2;i<=n&&pd;i++){
            for(int j=1;j<=m&&pd;j++){
                if(b[i-1][j]){
                    // printf("i:%d j:%d\n",i,j);
                    Change(i,j);nowans++;if(nowans>=ans) pd=0;
                }
            }
        }
        if(!pd) continue;
        bool op=1;
        for(int j=1;j<=m;j++) if(b[n][j]){op=0;break;}
        if(!op) continue;
        ans=Min(ans,nowans);
    }
    printf("%d\n",ans);
    return 0;
}

T3

这个题本来能打正解的,不过还是太懒了。

首先不难想到我们枚举异或出来的这个数是多少,然后直接把这个枚举出来的数与 \(a\) 异或,在哈希表里找 \(b\)。

一个更优的做法是我们枚举所有的 \(a\),然后把 \(a\) 和 \(a\) 改变一个二进制位的结果扔到哈希表里,然后枚举 \(b\),枚举 \(b\) 改变一个二进制位的结果扔到哈希表里。

注意,这种方法会导致一些重复,需要减去两个数相等的情况,然后把剩下的情况除以 \(2\)。

顺便学到了一种好的哈希表实现方法。

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 10001000
#define M 100010
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

struct Hash{
    const static int mod=10000849;
    int li[N],head[N],val[N],cnt[N],tot;
    inline void Insert(int v){
        int now=v%mod;
        for(int x=head[now];x;x=li[x]) if(val[x]==v){
            cnt[x]++;return;
        }
        val[++tot]=v;cnt[tot]=1;li[tot]=head[now];head[now]=tot;
    }
    inline int Find(int v){
        int now=v%mod;
        for(int x=head[now];x;x=li[x]) if(val[x]==v) return cnt[x];
        return 0;
    }
}h1,h2;

int n,m,a[M],b[M];
ll ans;

// #define fre
int main(){
    #ifdef fre
        freopen("my.in","r",stdin);
        freopen("my.out","w",stdout);
    #endif
    read(n);read(m);
    for(int i=1;i<=n;i++){
        read(a[i]);h2.Insert(a[i]);
    }
    for(int i=1;i<=m;i++) read(b[i]);
    for(int i=1;i<=n;i++)
        for(int j=0;j<=29;j++)
            h1.Insert(a[i]^(1<<j));
    for(int i=1;i<=m;i++)
        for(int j=0;j<=29;j++)
            ans=ans+h1.Find(b[i]^(1<<j));
    // printf("nowans:%lld\n",ans);
    for(int i=1;i<=m;i++){
        ans-=h2.Find(b[i])*30;
    }
    // printf("nowans:%lld\n",ans);
    assert(ans%2==0);
    ans=ans/2;
    printf("%lld\n",ans);
    return 0;
}

T4

这个题挺好的,有效锻炼了我的代码能力。

我们考虑对于一个字符串来说,其答案应该是跑完马拉车算法之后所有位置作为循环中心的最长回文长度之和。

接下来我们考虑修改,我们考虑预处理出把一个位置改成一个字符减少的和增加的分别是多少。

首先,减少了多少容易计算,不难发现,这个东西可以扫描线来做,比如说去掉位置 \(w\),那么答案就是把 \(w\) 右边的所有区间加入后 \(w\) 及其左边的值加上把 \(w\) 左边的所有区间加入后 \(w\) 及其右边的值。这个东西用线段树维护即可。

我们下面来考虑增加了多少,我们可以先预处理出最长回文区间端点在 \(i\) 左右的区间。然后枚举改成什么,预处理出哈希值,然后二分来判断,这里有一个正反哈希的技巧。预处理出来后直接枚举即可。

实际实现中要注意数组的意义一定要准确,不要随意更改意义,然后需要注意做马拉车时加上的哨兵字符。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define int long long
#define uint unsigned int
#define ull unsigned long long
#define N 200010
#define M number
using namespace std;
// #define debug
// #define fre

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

template<typename T> inline T Min(T a,T b){return a<b?a:b;}
template<typename T> inline T Max(T a,T b){return a<b?b:a;}

char s[N],t[N<<1];
int len;

struct Segment_Tree{
    #define ls k<<1
    #define rs k<<1|1
    struct node{
        int sum,lazy,size;
    }p[N<<2];
    inline void PushUp(int k){
        p[k].sum=p[ls].sum+p[rs].sum;p[k].size=p[ls].size+p[rs].size;
    }
    inline void BD(int k,int l,int r){
        if(l==r){p[k].size=1;return;}
        int mid=(l+r)>>1;BD(ls,l,mid);BD(rs,mid+1,r);PushUp(k);
    }
    inline void Build(){BD(1,0,len);}
    inline void A(int k,int x){p[k].sum+=p[k].size*x;p[k].lazy+=x;}
    inline void PushDown(int k){
        if(p[k].lazy){A(ls,p[k].lazy);A(rs,p[k].lazy);p[k].lazy=0;}
    }
    inline void AD(int k,int l,int r,int z,int y,int x){
        if(l==z&&r==y){A(k,x);return;}
        int mid=(l+r)>>1;PushDown(k);
        if(y<=mid) AD(ls,l,mid,z,y,x);
        else if(z>mid) AD(rs,mid+1,r,z,y,x);
        else AD(ls,l,mid,z,mid,x),AD(rs,mid+1,r,mid+1,y,x);
        PushUp(k);
    }
    inline void Add(int l,int r,int x){AD(1,0,len,l,r,x);}
    inline int GS(int k,int l,int r,int z,int y){
        if(l==z&&r==y) return p[k].sum;
        int mid=(l+r)>>1;PushDown(k);
        if(y<=mid) return GS(ls,l,mid,z,y);
        else if(z>mid) return GS(rs,mid+1,r,z,y);
        else return GS(ls,l,mid,z,mid)+GS(rs,mid+1,r,mid+1,y);
    }
    inline int GetSum(int l,int r){return GS(1,0,len,l,r);}
}seg1,seg2;

inline void Init(){
    scanf("%s",s);len=strlen(s);
    for(int i=0;i<=len-1;i++){
        t[i<<1]='#';t[i<<1|1]=s[i];
    }
    t[len<<1]='#';len<<=1;
    #ifdef debug
    printf("t:\n");
    for(int i=0;i<=len;i++) printf("%4lld",i);puts("");
    for(int i=0;i<=len;i++) printf("%4c",t[i]);puts("");
    #endif
    seg1.Build();seg2.Build();
}

int R[N],Rsum;

inline void Manacher(){
    int mid=0,MaxRight=0;
    for(int i=0;i<=len;i++){
        if(i<MaxRight) R[i]=Min(R[(mid<<1)-i],R[mid]+mid-i);else R[i]=1;
        while(t[i+R[i]]==t[i-R[i]]&&i+R[i]<=len&&i-R[i]>=0) R[i]++;
        if(MaxRight<R[i]+i){mid=i;MaxRight=R[i]+i;} 
    }
    for(int i=0;i<=len;i++) Rsum+=R[i]/2;
    #ifdef debug
    printf("Rsum:%lld\n",Rsum);
    printf("R:\n");
    for(int i=0;i<=len;i++) printf("i:%lld R:%lld\n",i,R[i]);puts("");
    #endif
}

//a数组指的是把某个位置上的数改成某个字母后减少的量。
//b数组恰恰相反,指的是把某个位置上的数改成某个字符后增加的量。
ll a[N<<1][26],b[N<<1][26];

inline void GetA(){
    ll sum=0;
    for(int i=len;i>=0;i--){
        if(t[i]!='#'){
            for(int j=0;j<=25;j++){
                ll nowans=seg1.GetSum(0,i);
                assert(nowans%2==0);
                a[i][j]+=nowans/2;
            }
        }
        #ifdef debug
        if(i>=81&&i-R[i]+1<=81){
            printf("spe:i:%lld\n",i);
            sum++;
        }
        #endif
        seg1.Add(i-R[i]+1,i,1);
    }
    for(int i=0;i<=len;i++){
        if(t[i]!='#'){
            for(int j=0;j<=25;j++){
                ll nowans=seg2.GetSum(i,len);
                assert(nowans%2==0);
                a[i][j]+=nowans/2;
            }
        }
        #ifdef debug
        if(i<=81&&i+R[i]-1>=81){
            printf("spe2:i:%lld\n",i);
            sum++;
        }
        #endif
        seg2.Add(0,i+R[i]-1,1);
    }
    #ifdef debug
    printf("spesum:%lld\n\n",sum);
    printf("a:\n");
    for(int i=0;i<=len;i++)
        for(int j=0;j<=25;j++) printf("i:%lld j:%lld a:%lld\n",i,j,a[i][j]);
    puts("");
    #endif
}

ull h1[N],h2[N],Pow[N];
const int base=29;

inline void GetH(){
    h1[0]=t[0];
    for(int i=1;i<=len;i++) h1[i]=h1[i-1]*base+t[i];
    h2[len]=t[len];
    for(int i=len-1;i>=0;i--) h2[i]=h2[i+1]*base+t[i];
    Pow[0]=1;
    for(int i=1;i<=len;i++) Pow[i]=Pow[i-1]*base;
}

inline ull GetH1(int l,int r){
    return h1[r]-h1[l-1]*Pow[r-l+1];
}

inline ull GetH2(int l,int r){
    return h2[l]-h2[r+1]*Pow[r-l+1];
}

//vi里面是最长回文串以 i 这个位置为端点的中心点的集合。
vector<int> v[N];

inline bool Check(int l,int r,int z,int y){
    if(l<0||z<0||r>len||y>len) return 0;
    return GetH1(l,r)==GetH2(z,y);
}

inline int Binary(int z,int y){
    if(t[z]!=t[y]) return 0;
    int l=2,r=Min(z+1,len-y+1),ans=1;
    while(l<=r){
        int mid=(l+r)>>1;
        if(Check(z-mid+1,z-1,y+1,y+mid-1)){ans=mid;l=mid+1;}
        else r=mid-1;
    }
    return ans;
}

//我们先来得到改变左端点的答案,然后在得到改变右端点的答案。
inline void GetB(){
    for(int i=0;i<=len;i++){
        if(i-R[i]>=0)
        v[i-R[i]].push_back(i);
    }
    for(int i=0;i<=len;i++){
        for(int j=0;j<v[i].size();j++){
            int now=v[i][j];
            char last=t[i];
            for(int k=0;k<=25;k++){
                t[i]=k+'a';
                int nowans=Binary(i,now+R[now]);
                assert(nowans%2==0);
                b[i][k]+=nowans/2;
            }
            t[i]=last;
        }
    }
    for(int i=0;i<=len;i++) v[i].clear();
    for(int i=len;i>=0;i--){
        v[i+R[i]].push_back(i);
    }
    for(int i=0;i<=len;i++){
        for(int j=0;j<v[i].size();j++){
            int now=v[i][j];
            char last=t[i];
            for(int k=0;k<=25;k++){
                t[i]=k+'a';
                int nowans=Binary(now-R[now],i);
                assert(nowans%2==0);
                b[i][k]+=nowans/2;
            }
            t[i]=last;
        }
    }
    #ifdef debug
    printf("b:\n");
    puts("");
    for(int i=0;i<=len;i++)
        for(int j=0;j<=25;j++) printf("i:%lld j:%lld b:%lld\n",i,j,b[i][j]);
    puts("");
    #endif
}

ll ans=-INF;

inline void GetAns(){
    ans=Rsum;
    int ansi=-1,ansj=-1;
    for(int i=0;i<=len;i++){
        for(int j=0;j<=25;j++){
            // ans=Max(ans,Rsum-a[i][j]+b[i][j]);
            if(ans<Rsum-a[i][j]+b[i][j]){
                ans=Rsum-a[i][j]+b[i][j];
                ansi=i;ansj=j;
            }
        }
    }
    #ifdef debug
    printf("ansi:%lld ansj:%lld\n",ansi,ansj);
    #endif
}

inline void Print(){
    #ifdef debug
    printf("ans:");
    #endif
    printf("%lld\n",ans);
}

signed main(){
    #ifdef fre
    freopen("my.in","r",stdin);
    freopen("my.out","w",stdout);
    #endif
    Init();Manacher();GetA();GetH();
    GetB();GetAns();Print();
    return 0;
}
上一篇:noip模拟59(待补)


下一篇:noip模拟测试62