20241002测试

move

题面:
\(T\) 组数据,每组数据有 \(n\) 个数轴上的点 \(a_1,a_2,\dots,a_n\)。从原点开始,每次选择一个点未被选择过的点,如果当前在这个点上,那么分数加 \(1\),否则向这个点移动 \(1\) 格。问最高分数。
题解:
容易发现,要么先往左再往右,要么先往右再往左。先考虑第一种情况,枚举左端点,二分往右最远能到多远.
时间复杂度 \(\Theta(\sum n\log \sum n)\)
代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
template<typename T>inline void read(T&x){
    x=0;
    bool f=0;
    char c=getchar();
    for(;c<'0'||c>'9';c=getchar())if(c=='-')f=1;
    for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+(c^48);
    if(f)x=-x;
}
inline void Max(int&x,int y){
    x<y&&(x=y);
}
const int N=300005;
int T,n,a[N],b[N],cnta,cntb,cnt0,ans;
int main(){
    // freopen("move.in","r",stdin),freopen("move.out","w",stdout);
    for(read(T);T--;){
        read(n),cnta=cntb=cnt0=ans=0;
        for(int i=1,x;i<=n;i++){
            read(x);
            if(x<0)b[++cntb]=-x;
            if(x==0)cnt0++;
            if(x>0)a[++cnta]=x;
        }
        std::reverse(b+1,b+cntb+1);
        for(int i=0;i<=cntb;i++){
            if(cntb-i<b[i])break;
            int l=0,r=cnta,res=l;
            for(;l<=r;){
                int mid=(l+r)>>1;
                if(cnta-mid>=a[mid]+b[i])res=mid,l=mid+1;
                else r=mid-1;
            }
            Max(ans,i+res);
        }
        for(int i=0;i<=cnta;i++){
            if(cnta-i<a[i])break;
            int l=0,r=cntb,res=l;
            for(;l<=r;){
                int mid=(l+r)>>1;
                if(cntb-mid>=b[mid]+a[i])res=mid,l=mid+1;
                else r=mid-1;
            }
            Max(ans,i+res);
        }
        printf("%d\n",ans+cnt0);
    }
    return fflush(stdout),fclose(stdin),fclose(stdout),0;
}

string

题面:
\(T\) 组数据,每组数据两个字符串 \(A\)\(B\),已知 \(S\) 满足,\(A\)\(B\) 都是 \(S\) 的子串且出现相同次数,求 \(S\) 的最短长度,无解输出 \(-1\)
题解:
分成三种情况考虑:
如果说 \(A\)\(B\) 中出现了多次或者 \(B\)\(A\) 中出现了多次,那么无解。
如果 \(A\)\(B\) 中出现了一次,答案为 \(|B|\),如果 \(B\)\(A\) 中出现了一次,答案为 \(|A|\)
否则一定是将两个串的最长公共部分拼接起来。同时为 \(A\) 的前缀和 \(B\) 的后缀的最长串为 \(C_1\),反过来的为 \(C_2\),那么两个答案为 \(|A|+|B|-\max(|C_1|,|C_2|)\)
考虑使用 hash 实现,复杂度为 \(\text O(\sum|A|+ \sum|B|)\)
代码:

#include<cstdio>
#include<cstring>
typedef unsigned long long ull;
const int N=1000005;
ull base1=998244353,base2=1000000007,mod=1000000009;
int T,n,m;
char a[N],b[N],ab[N<<1],ba[N<<1];
struct Hash{
    ull h1,h2;
    bool operator==(Hash b){
        return h1==b.h1&&h2==b.h2;
    }
}hab[N<<1],hba[N<<1];
ull pow1[N<<1],pow2[N<<1];
inline Hash add(Hash x,char y){
    return{(x.h1*base1+(ull)y)%mod,(x.h2*base2+(ull)y)%mod};
}
inline Hash get(Hash*x,int l,int r){
    return{(x[r].h1+mod-x[l-1].h1*pow1[r-l+1]%mod)%mod,(x[r].h2+mod-x[l-1].h2*pow2[r-l+1]%mod)%mod};
}
inline int max(int x,int y){
    return x>y?x:y;
}
int main(){
    // freopen("string.in","r",stdin),freopen("string.out","w",stdout);
    for(scanf("%d",&T);T--;){
        scanf("%s%s",a+1,b+1),n=strlen(a+1),m=strlen(b+1);
        ab[n+1]=ba[m+1]='#',pow1[0]=pow2[0]=1;
        for(int i=1;i<=n;i++)ab[i]=a[i],ba[m+i+1]=a[i];
        for(int i=1;i<=m;i++)ab[n+i+1]=b[i],ba[i]=b[i];
        for(int i=1;i<=n+m+1;i++){
            pow1[i]=(pow1[i-1]*base1)%mod,pow2[i]=(pow2[i-1]*base2)%mod;
            hab[i]=add(hab[i-1],ab[i]),hba[i]=add(hba[i-1],ba[i]);
        }
        {
            int cntab=0,cntba=0;
            for(int i=n*2+1;i<=n+m+1;i++)if(hab[n]==get(hab,i-n+1,i))cntab++;
            for(int i=m*2+1;i<=n+m+1;i++)if(hba[m]==get(hba,i-m+1,i))cntba++;
            if(cntab>1||cntba>1){
                puts("-1");
                continue;
            }
            if(cntab==1){
                printf("%d\n",m);
                continue;
            }
            if(cntba==1){
                printf("%d\n",n);
                continue;
            }
        }
        int res1=0,res2=0;
        for(int i=1,j=n+m+1;i<=n&&j>n+1;i++,j--)if(hab[i]==get(hab,j,n+m+1))res1=i;
        for(int i=1,j=n+m+1;i<=m&&j>m+1;i++,j--)if(hba[i]==get(hba,j,n+m+1))res2=i;
        printf("%d\n",n+m-max(res1,res2));
    }
    return fflush(stdout),fclose(stdin),fclose(stdout);
}

game

题面:
\(n\) 轮石头剪刀布,对方第 \(i\) 轮的策略 \(b_i\) 可以是 \(\{-1,0,1,2\}\) 分别表示任意一种,石头,布,剪刀。现在要在连续两轮不能出相同手势的限制下求出最多的胜场,由于对方可以出 \(-1\),要求出所有情况下的答案的总和。
题解:
这题有点难度,不一定写的清楚。
考虑动态规划,定义状态 \(f(i,s_0,s_1,s_2)\) 表示和第 \(i\) 个对手出 \(0,1,2\) 的最大分数分别为 \(s_0,s_1,s_2\) 方案数。
若第 \(b_i\) 若能取到 \(0\)(即为 \(0\)\(-1\))则有:

\[f(i-1,s_0,s_1,s_2)\xrightarrow{\sum} f(i,\max(s_1,s_2),\max(s_0,s_2),-\infty) \]

\(b_i\) 能取到 \(1\)\(2\) 的方案同理。此时答案为 \(\sum\left(\max(s_0,s_1,s_2)\times f(n,s_0,s_1,s_2)\right)\)
目前复杂度 \(\text O(n^4)\),考虑优化。
发现一定有一维为 \(-\infty\)。定义状态 \(f(i,s_0,s_1,k)\) 表示 \(k\) 必输时,本轮平局或赢的得分分别为 \(s_0,s_1\) 的方案数。
此时答案转化为 \(\sum\left(\max(s_0,s_1)\times f(n,s_0,s_1,k)\right)\)
考虑转移,若 \(b_i\) 可以出 \(0\),那么有如下情况:
如果 \(b_{i-1}\) 可以是 \(0\) 那么有:

\[f(i-1,s_0,s_1,2)\xrightarrow{\sum} f(i,s_1,s_0+1,2) \]

如果 \(b_{i-1}\) 可以是 \(1\) 那么有:

\[f(i-1,s_0,s_1,0)\xrightarrow{\sum} f(i,\max(s_0,s_1),s_1+1,2) \]

如果 \(b_{i-1}\) 可以是 \(2\) 那么有:

\[f(i-1,s_0,s_1,1)\xrightarrow{\sum} f(i,s_0,\max(s_0,s_1)+1,2) \]

\(b_i\) 可以出 \(1\)\(2\) 的情况同理。
此时时间复杂度 \(\text O(n^3)\),可以继续优化。
由于答案式子为 \(\sum\left(\max(s_0,s_1)\times f(n,s_0,s_1,k)\right)\) 考虑该式子,下面令 \(s_0'\)\(s_1'\)\(k'\) 表示能转移到 \(s_0\)\(s_1\)\(k\) 的状态。

\[\begin{align*} \sum\left(\max(s_0,s_1)\times f(i,s_0,s_1,k)\right)= &\sum\max(s_0,s_1)\times\sum f(i-1,s_0',s_1',k')\\ =&\sum\max(s_0',s_1')\times f(i-1,s_0',s_1',k')\\ +&\sum(\max(s_0,s_1)-\max(s_0',s_1'))\times f(i-1,s_0',s_1',k')\\ \end{align*} \]

每次修改只用考虑后面部分的增量即可。
定义 \(\Delta(s_0',s_1')=\max(s_0,s_1)-\max(s_0',s_1')\) 那在如上 \(k=2\) 时的三种情况分别对应:
\(\Delta(s_0,s_1)=[s_0\ge s_1]\)\(\Delta(s_0,s_1)=[s_0\le s_1]\)\(\Delta(s_0,s_1)=1\)
修改状态定义 \(f(i,t,k)\) 表示 \(k\) 必输时,本轮平局或赢的得分 \(s_0,s_1\) 满足 \(s_0-s_1=t\) 的方案数。
可以得出状态转移方程并在转移的同时得到答案,下面继续考虑 \(k=2\) 的情况,下面 \(\Delta\)表示上面三种情况不同的\(\Delta(s_0,s_1)\)
如果 \(b_{i-1}\) 可以是 \(0\) 那么有:

\[f(i-1,t,2)\xrightarrow{\sum} f(i,-t+1,2),f(i-1,t,2)\times\Delta\xrightarrow{\sum}ans \]

如果 \(b_{i-1}\) 可以是 \(1\) 那么有:

\[f(i-1,t,0)\xrightarrow{\sum} f(i,\max(t,0)-1,2),f(i-1,t,0)\times\Delta\xrightarrow{\sum}ans \]

如果 \(b_{i-1}\) 可以是 \(2\) 那么有:

\[f(i-1,t,1)\xrightarrow{\sum} f(i,\min(t,0)-1,2),f(i-1,t,1)\times\Delta\xrightarrow{\sum}ans \]

\(b_i\) 可以出 \(1\)\(2\) 的情况同理。
此时的时间复杂度终于化为了 \(\Theta(n^2)\)

#include<cstdio>
#define int long long
template<typename T>inline void read(T&x){
    x=0;
    bool f=0;
    char c=getchar();
    for(;c<'0'||c>'9';c=getchar())if(c=='-')f=1;
    for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+(c^48);
    if(f)x=-x;
}
const int N=2005,mod=998244353;
int n,b[N],data[N][3][N<<1],*f[N][3],ans,get[3][3]={0,-1,1,1,0,-1,-1,1,0};
inline void Add(int&x,int y){(x+=y)>=mod&&(x-=mod);}
inline int mul(int x,int y){return x*y%mod;}
inline void Mul(int&x,int y){x=mul(x,y);}
inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x<y?x:y;}
inline int add(int x,int y){return Add(x,y),x;}
signed main(){
    // freopen("game.in","r",stdin),freopen("game.out","w",stdout);
    read(n);
    for(int i=1;i<=n;i++)read(b[i]);
    for(int i=1;i<=n;i++)for(int j=0;j<3;j++)f[i][j]=data[i][j]+N;
    if(~b[1])f[1][b[1]][1]=1,Add(ans,1);
    else for(int i=0;i<3;i++)f[1][i][1]=1,Add(ans,1);
    for(int i=2;i<=n;i++){
        if(b[i]==-1)Mul(ans,3);
        for(int lst=0;lst<3;lst++)for(int delta=-n;delta<=n;delta++)if(f[i-1][lst][delta]){
            int val=f[i-1][lst][delta];
            for(int nw=0;nw<3;nw++)if(b[i]==-1||b[i]==nw){
                int nwdelta;
                if(get[lst][nw]<0)Add(ans,val),nwdelta=max(delta,0)+1;
                else if(get[lst][nw]==0)Add(ans,(delta<=0)*val),nwdelta=-delta+1;
                else Add(ans,(delta>=0)*val),nwdelta=min(delta,0)+1;
                Add(f[i][nw][nwdelta],val);
            }
        }
    }
    printf("%lld\n",ans);
    return fflush(stdout),fclose(stdin),fclose(stdout),0;
}
posted @ 2024-10-02 19:48  junjunccc  阅读(4)  评论(0编辑  收藏  举报