CF785

A

题意:

给定一长度为\(n\),由小写字母构成的字符串,\(a\)的分数是\(1\)\(b\)的分数是\(2\),……,\(z\)的分数是\(26\)\(Alice\)\(Bob\)玩游戏,\(Alice\)可以选择长度为偶数的子串删去,\(Bob\)可以选择长度为奇数的子串删去,并得到相应的分数。\(Alice\)先手,二人轮流操作,谁会赢,赢家比输家最多高多少分?

题解:

特判\(n=1\)\(Bob\)

否则\(Alice\)赢。

如果\(n\)是偶数,\(Alice\)拿走全部

否则\(Alice\)\([1\sim n-1]\)\([2\sim n]\)中选择分数高的一段。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=1e6+10,mod=1e4+7,inf=2e9;
    int n,m,sum;
    char s[N];
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int T;cin>>T;
        while(T--)
        {
            cin>>s;
            n=strlen(s);sum=0;
            for(int i=0;i<n;++i) sum+=s[i]-'a'+1;
            if(n==1)
            {
                cout<<"Bob ";
                cout<<s[0]-'a'+1<<'\n';
                continue;
            }
            cout<<"Alice ";
            if(n%2==0)
            {
                cout<<sum<<'\n';
            }
            else
            {
                sum-=2*min(s[0]-'a'+1,s[n-1]-'a'+1);
                cout<<sum<<'\n';
            }
        }
    }
}
signed main()
{
    red::main();
    return 0;
}
/*

*/

B

题意:

给定一字符串,要求满足\(f(t,u,v)\),即在字符串的任意子串\(t\)中,任选原串出现过的两个字符,都要满足两个这字符在这个子串中出现的个数之差小于等于\(1\)。问这个字符串能否满足要求?

题解:

如果字符串要满足要求,这个字符串必须有循环节。而且循环节内不能有重复字符。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=1e6+10,mod=1e4+7,inf=2e9;
    int n,m;
    char s[N];
    int col;
    map<int,int> q;
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int T;cin>>T;
        while(T--)
        {
            cin>>s;n=strlen(s);
            q.clear();
            int len=0;
            bool flag=0;
            for(int i=0;i<n;++i)
            {
                if(q[s[i]])
                {
                    if(q[s[i]]!=1) flag=1;
                    len=i;
                    break;
                }
                q[s[i]]=1;
            }
            for(int i=0;i<len;++i)
            {
                int j=i;
                while(j<n)
                {
                    if(s[i]!=s[j]) flag=1;
                    j+=len;
                }
            }
            if(flag) cout<<"NO\n";
            else cout<<"YES\n";
        }
    }
}
signed main()
{
    red::main();
    return 0;
}
/*

*/

C

题意:

问一个数字\(n(1\leq n\leq 4*10^4)\)有几种拆分成回文数字相加的方法,比如\(121\)是回文数字。

题解:

打个表发现\(40000\)以内的回文数只有\(500\)

\(dp[i][j]\)表示数字\(i\),只用大于等于第\(j\)个回文数来拆分,得到的方案数,做个后缀和就可以\(O(1)\)转移了。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=40000+10,mod=1e9+7,inf=2e9;
    int n,m;
    int f[N];
    int st[N],top;
    int g[N],num;
    int dp[N][501];
    inline bool check(int x)
    {
        int y=x;
        top=0;
        while(x) st[++top]=x%10,x/=10;
        int z=0;
        for(int i=1;i<=top;++i) z=z*10+st[i];
        return (z==y);
    }
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int T;cin>>T;
        for(int j=0;j<=500;++j) dp[0][j]=1;
        for(int i=1;i<=40000;++i)
        {
            if(check(i))
            {
                g[++num]=i;
            }
            for(int j=1;j<=num;++j)
            {
                dp[i][j]=dp[i-g[j]][j];
            }
            for(int j=num;j>=1;--j) dp[i][j]=(dp[i][j]+dp[i][j+1])%mod;
        }
        while(T--)
        {
            cin>>n;
            cout<<dp[n][1]<<'\n';
        }
    }
}
signed main()
{
    red::main();
    return 0;
}
/*

*/

D

题意:

已知等差数列\(C\)是等差数列\(A,B\)的所有公共项,问有多少种可能的等差数列\(A\)

题解:

第一步是判断\(C\)是不是\(B\)的真正的子序列。

判断方法是,\(C\)的首项和尾项有没有在\(B\)中出现过,\(C\)的公差是不是\(B\)的倍数。

第二步判断是不是有无限种,方法是看\(C\)首项的前一项和尾项的后一项有没有在\(B\)中出现过。

最后一步是计算种类数

\(A\)的公差只可能是\(C\)的因子,因此只要枚举\(A\)的公差,同时\(A,B\)公差的最小公倍数必须是\(C\)的公差,否则它们两个就会在\(C\)相邻两项之间相交。

\(A\)的可能的种类数就是\(C\)首尾之外的发散项,其实就是\(C\)的首项到上一项之间。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=1e6+10,mod=1e9+7,inf=2e9;
    int n,m,ans;
    int a0,b0,c0,q1,q2,q3;
    inline bool checkbc()
    {
        int s=b0;
        int l=0,r=n-1,k=0;
        while(l<=r)
        {
            if(s+mid*q2<=c0) k=mid,l=mid+1;
            else r=mid-1;
        }
        if(s+k*q2!=c0) return 0;
        l=0,r=n-1,k=n-1;
        while(l<=r)
        {
            if(s+mid*q2>=c0+(m-1)*q3) k=mid,r=mid-1;
            else l=mid+1;
        }
        if(s+k*q2!=c0+(m-1)*q3) return 0;
        if(q3%q2!=0) return 0;
        return 1;
    }
    inline int lcm(int x,int y)
    {
        return x*y/__gcd(x,y);
    }
    inline void work(int l,int r,int tl,int tr,int x)
    {
        // cout<<x<<"!!"<<endl;
        //cout<<tl<<' '<<l<<"!!!"<<endl;
        int k=(tl-l)/x;
        //cout<<x<<' '<<k<<"!!"<<endl;
        ans=(ans+k*k)%mod;
    }
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int T;cin>>T;
        while(T--)
        {
            cin>>b0>>q2>>n;
            cin>>c0>>q3>>m;
            ans=0;
            if(!checkbc()||c0<b0||(c0+(m-1)*q3)>(b0+(n-1)*q2))
            {
                cout<<"0"<<'\n';
                continue;
            }
            if(c0-q3<b0||(c0+m*q3)>(b0+(n-1)*q2))
            {
                cout<<"-1\n";
                continue;
            }
            //ans=1;
            for(int q1=1;q1*q1<=q3;++q1)
            {
                if(q3%q1!=0) continue;
                int x=q1,y=lcm(x,q2);
                if(y==q3) work(c0-q3,c0+m*q3,c0,c0+(m-1)*q3,x);
                if(q1*q1==q3) continue;
                x=q3/q1,y=lcm(x,q2);
                //if(x==q3) cout<<y<<' '<<q3<<"!!!!!!!!!!!!!"<<endl;
                if(y==q3) work(c0-q3,c0+m*q3,c0,c0+(m-1)*q3,x);
            }
            cout<<ans<<'\n';
        }
    }
}
signed main()
{
    red::main();
    return 0;
}
/*
1
-685761754 232786464 375044871
-220188826 232786464 262040392

*/

E

给定序列\(B(1\leq b_i<2^{20})\),长度为\(n(1\leq n\leq 2^{20})\),真实序列是\(A_i=2^{B_i}\)

\(E=A_1*A_2*……*A_n\)

现在要给每个\(*\)设计一个意义,要么是异或,要么是幂次,幂次的运算优先级高于异或。

至少要把\(m\)\(*\)设计乘异或。

求所有可能的设计方案的\(E\)的值的异或和,以二进制形式输出,答案对\(2^{2^{20}}\)取模。

题解:

由于幂次优先级高于异或,所以等于是通过异或符号把序列分隔成至少\(m+1\)段。这可以用隔板法解决。

每一段之间都是幂次运算,运算结果都是二的整幂次。

可以直接哪一段作为未分割的一段,计算这段的出现次数。

可以发现,\(b_i>=1\),也就是说这个长度不会超过\(20\),否则就会被模掉。

而且在隔板法中,把\(n\)个数分成至少\(m\)段也只会有\(20\)种不同的\(n,m\),所以可以算组合数。

但是这种级别的组合数没有模数很难搞定,好在只需要知道奇偶性,所以设计\(s[i]\)表示\(i!\)中乘过多少个\(2\)

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=2e6+10,mod=(1<<20),inf=2e9;
    int n,m;
    int b[N];
    int s[N];
    int ans[N];
    inline int C(int n,int m)
    {
        if(n<m) return 0;
        int s1=s[n],s2=s[n-m]+s[m];
        if(s1>s2) return 0;
        return 1;
    }
    typedef pair<int,int> pr;
    map<pr,int> q;
    inline int work(int n,int m)
    {
        m=max(0ll,m);
        if(n<m) return 0;
        //cout<<n<<' '<<m<<"!!!!!!!!"<<endl;
        if(q[pr(n,m)]) return q[pr(n,m)];
        int s=0;
        for(int k=m;k<=n;++k) s+=C(n,k);
        if(s%2==0) s=2;
        else s=1;
        return (q[pr(n,m)]=s);
    }
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        cin>>n>>m;++m;
        for(int i=2;i<=n;i*=2)
        {
            for(int j=1;i*j<=n;++j)
            {
                ++s[i*j];
            }
        }
        for(int i=1;i<=n;++i) s[i]+=s[i-1];
        for(int i=1;i<=n;++i)
        {
            cin>>b[i];
        }
        for(int l=1;l<=n;++l)
        {
            int r=l,k=b[l];
            while(k<mod)
            {
                int tmp=work(n-1-(r-l+2)+(l==1)+(r==n),m-1-2+(l==1)+(r==n));
                //if(r-l+1==3)cout<<l<<' '<<r<<' '<<tmp<<"!!"<<endl;
                //cout<<n-1-(r-l+2)+2*(l==1)+2*(r==n)<<' '<<m-1-2+2*(l==1)+2*(r==n)<<' '<<k<<"!!!"<<endl;
                if(tmp%2==1) ans[k]^=1;
                ++r;
                if(b[r]>=20||r>n) break;
                int op=(1<<b[r]);
                k*=op;
            }
        }
        bool flag=0;
        for(int i=mod-1;i>=0;--i)
        {
            if(ans[i]==1||i==0) flag=1;
            if(flag) cout<<ans[i];
        }
        cout<<'\n';
    }
}
signed main()
{
    red::main();
    return 0;
}
/*
10 3
1 1 1 1 1 1 1 1 1 1

*/

F

题意:

给定\(n*n(2\leq n\leq 32)\)的网格,给每条边一个权值,总和不能超过\(48000\)

有一个小偷从\((1,1)\),陆续偷盗\(m\)个地点,他每经过一条边,一个计数器就会让数字异或上这条边的长度,偷完之后会输出数字并清空计数器。

现在你要给每条边安排一个权值,然后每次计数器会给你一个数字,根据这个数字判断出小偷偷走了哪个坐标的物品。

题解:

有很多种方法设计权值,但是要总和不超过\(48000\)很难。

考虑一种从\(0\sim 2^k-1\)唯一分布的数码格雷码,因为格雷码有一个特点,最零位翻转\(2^{k-1}\)次,第一位翻转\(2^{k-2}\)次……最高位翻转\(1\)次,也就是说越低位在异或时,出现次数越多,刚好可以最小化路径总长度。

二维格雷码可以简化,将\(g(i)^2\)设计为横向边权,将\(2*g(i)^2\)设计为纵向边权,总和刚好小于\(48000\)

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=35,mod=1e4+7,inf=2e9;
    int n,m,k;
    int a[N][N],b[N][N];
    int ansx[N][N],ansy[N][N];
    int stx,sty;
    inline int pow(int n)
    {
        int x=1;    
        while(n%2==0)
        {
            x<<=1;
            n>>=1;
        }
        return x;
    }
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int T=1;
        while(T--)
        {
            cin>>n>>m;int sum=0;
            for(int i=1;i<=n;++i)
            {
                for(int j=1;j<n;++j)
                {
                    a[i][j]=pow(j)*pow(j);
                    ansx[i][j+1]=ansx[i][j]^a[i][j];
                    sum+=a[i][j];
                }
            }
            for(int j=1;j<=n;++j)
            {
                for(int i=1;i<n;++i)
                {
                    b[i][j]=2*pow(i)*pow(i);
                    ansy[i+1][j]=ansy[i][j]^b[i][j];
                    sum+=b[i][j];
                }
            }
            for(int i=1;i<=n;++i)
            {
                for(int j=1;j<n;++j)
                {
                    cout<<a[i][j];
                    if(j==n-1) cout<<endl;
                    else cout<<' ';
                }
            }
            for(int i=1;i<n;++i)
            {
                for(int j=1;j<=n;++j)
                {
                    cout<<b[i][j];
                    if(j==n) cout<<endl;
                    else cout<<' ';
                }
            }
            //cout<<sum<<"!!!"<<endl;
            stx=sty=1;
            //cout<<ansy[1][2]<<' '<<ansy[2][2]<<"!!"<<endl;
            while(m--)
            {
                int x;cin>>x;
                bool flag=0;
                for(int i=1;i<=n&&!flag;++i)
                {
                    for(int j=1;j<=n&&!flag;++j)
                    {
                        int l=sty,r=j;
                        if(l>r) swap(l,r);
                        int tmp=ansx[i][l]^ansx[i][r];
                        l=stx,r=i;
                        if(l>r) swap(l,r);
                        tmp^=ansy[l][j]^ansy[r][j];
                        //cout<<stx<<' '<<sty<<' '<<i<<' '<<j<<' '<<tmp<<"!!"<<endl;
                        if(tmp==x)
                        {
                            flag=1;
                            stx=i,sty=j;
                            cout<<i<<' '<<j<<endl;
                            break;
                        }
                    }
                }
            }
        }
    }
}
signed main()
{
    red::main();
    return 0;
}
/*

*/
posted @ 2022-05-02 21:05  lovelyred  阅读(61)  评论(0)    收藏  举报