牛客OI赛制测试赛3游记

A - 数字权重

题目大意:

一个\(n\)位的数字。设第\(i\)位的数为\(a_i\),其中\(a_1\)为最高位,\(a_n\)为最低位,\(k\)为给定的数字。求同时满足满足以下两个条件的数的个数:

  1. 不含前导\(0\)
  2. \(\sum_{i=2}^n(a_i-a_{i-1})=k\)

\(n,|k|\le10^{13}\)

思路:

对和式稍加变形得\(a_n-a_1=k\),因此我们只需要考虑\(a_1\)\(a_n\),其余数位\(0\sim 9\)随便放即可。

时间复杂度\(\mathcal O(1)\)

源代码:

#include<cstdio>
#include<cctype>
#include<algorithm>
using int64=long long;
inline int64 getint() {
    register char ch;
    register bool neg=false;
    while(!isdigit(ch=getchar())) neg|=ch=='-';
    register int64 x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return neg?-x:x;
}
constexpr int mod=1e9+7;
inline int power(int a,int64 k) {
    int ret=1;
    for(;k;k>>=1) {
        if(k&1) ret=(int64)ret*a%mod;
        a=(int64)a*a%mod;
    }
    return ret;
}
int main() {
    const int64 n=getint(),k=getint();
    if(std::abs(k)>9) {
        puts("0");
        return 0;
    }
    int ans=power(10,n-2);
    if(k>=0) ans=ans*(9-k)%mod;
    if(k<0) ans=ans*(10+k)%mod;
    printf("%d\n",ans);
    return 0;
}

B - 毒瘤xor

同CC-XXOR。

C - 硬币游戏

题目大意:

A和B各有一行长度为\(2n(n\le10^6)\)的01序列,A先取,每次只能取两个人之前都没取过的位置,这样\(n\)次后两个人就得到了长度为\(n\)的数字串,谁的字典序大谁就赢了。若两人都按照最优策略,问最后谁赢(或平手)。

思路:

每次贪心取未被选择位置上的\(1\),如果有很多\(1\)那么优先取对方也是\(1\)的。用堆来维护,时间复杂度\(\mathcal O(n\log n)\)

源代码:

#include<queue>
#include<cstdio>
#include<cctype>
inline int getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
inline bool getval() {
    register char ch;
    while(!isalpha(ch=getchar()));
    return ch=='U';
}
const int N=2e6+1;
bool a[N],b[N],mark[N];
struct Node {
    bool a,b;
    int id;
    bool operator < (const Node &rhs) const {
        if(a^rhs.a) return !a;
        if(b^rhs.b) return !b;
        return id<rhs.id;
    }
};
std::priority_queue<Node> q[2];
int main() {
    const int n=getint();
    for(register int i=1;i<=n*2;i++) a[i]=getval();
    for(register int i=1;i<=n*2;i++) b[i]=getval();
    for(register int i=1;i<=n*2;i++) {
        q[0].push((Node){a[i],b[i],i});
        q[1].push((Node){b[i],a[i],i});
    }
    for(register int i=1;i<=n;i++) {
        while(mark[q[0].top().id]) q[0].pop();
        a[i]=q[0].top().a;
        mark[q[0].top().id]=true;
        while(mark[q[1].top().id]) q[1].pop();
        b[i]=q[1].top().a;
        mark[q[1].top().id]=true;
    }
    for(register int i=1;i<=n;i++) {
        if(a[i]==b[i]) continue;
        puts(a[i]?"clccle trl!":"sarlendy tql!");
        return 0;
    }
    puts("orz sarlendy!");
    return 0;
}

D - 粉樱花之恋

题目大意:

求斐波那契数列前\(n(n\le10^{18})\)项和。

思路:

找规律可以发现前\(n\)项和\(F(n)=f(n+3)-1\),因此直接矩阵快速幂或者折半公式即可。

时间复杂度\(\mathcal O(n\log n)\)

源代码:

#include<cstdio>
#include<cctype>
#include<unordered_map>
using int64=long long;
inline int64 getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int64 x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
const int64 mod=998244353;
std::unordered_map<int64,int> m;
inline int64 sqr(const int64 &x) {
    return x*x;
}
int64 f(const int64 &n) {
    if(m[n]) return m[n];
    if(n==1||n==2) return m[n]=1;
    if(n&1) return m[n]=(sqr(f(n/2+1))+sqr(f(n/2)))%mod;
    return m[n]=(f(n/2-1)*2+f(n/2))*f(n/2)%mod;
}
int main() {
    printf("%lld\n",f(getint()+3)-1);
    return 0;
}

E - 符合条件的整数:

题目大意:

\([2^n,2^m)(n,m\le65)\)内满足\(x\equiv1\pmod{7}\)\(x\)个数。

思路:

使用__int128直接算即可。

源代码:

#include<cstdio>
#include<cctype>
#include<iostream>
typedef unsigned __int128 uint128;
std::ostream &operator <<(std::ostream &os,const uint128 &x) {
    if(x>=10) os<<x/10;
    return os<<int(x%10);
}
inline int getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
int main() {
    const int n=getint(),m=getint();
    std::cout<<(((uint128)1<<m)-2)/7-(((uint128)1<<n)-2)/7<<std::endl;
    return 0;
}

F - 可爱即正义

题目大意:

给你一个字符串\(s\),问是否能通过交换两个字符使得其不包括suqingnianloveskirito作为子串。

思路:

哈希以后统计suqingnianloveskirito的出现次数,并记录出现位置,分情况讨论即可。

源代码:

明明很多锅(比如\(cnt=0\)时交换前两个可能产生新的suqingnianloveskirito),但还是过了。

#include<cstdio>
#include<cstring>
using uint64=unsigned long long;
const int N=1e6+2,base=31;
char s[N];
const char t[23]="\0suqingnianloveskirito";
uint64 hash[N],pwr[N],tmp;
int pos[N];
inline uint64 calc(const int &i) {
    return hash[i]-hash[i-21]*pwr[21];
}
int main() {
    scanf("%s",&s[1]);
    const int n=strlen(&s[1]);
    if(n==1) {
        puts("No");
        return 0;
    }
    for(register int i=pwr[0]=1;i<=n;i++) {
        pwr[i]=pwr[i-1]*base;
        hash[i]=hash[i-1]*base+s[i]-'a'+1;
    }
    for(register int i=1;i<=21;i++) {
        tmp=tmp*base+t[i]-'a'+1;
    }
    int cnt=0;
    for(register int i=1;i<=n;i++) {
        if(i>=21&&calc(i)==tmp) {
            pos[++cnt]=i;
        }
    }
    if(cnt==0) {
        puts("Yes\n1 2\n");
        return 0;
    }
    if(cnt==1) {
        puts("Yes");
        printf("%d %d\n",pos[1],pos[1]-1);
        return 0;
    }
    if(cnt==2) {
        puts("Yes");
        printf("%d %d\n",pos[1],pos[2]-1);
        return 0;
    }
    puts("No");
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/skylee03/p/9637996.html