nowcoder(牛客网)OI测试赛3 解题报告 T1 数字权重(快速幂) T2 毒瘤XOR(异或)(二进制) T3 硬币游戏(贪心) T4 粉樱花之恋(矩阵快速幂)(斐波那契数列) T5 符合条件的整数(高精度) T6 可爱即正义(字符串)

昨天因为胡搞了一会儿社团的事情,所以错过(逃过)了nowcoder的测试赛。。。。。
以上,听说还是普及组难度qwq,而且还有很多大佬AK(然而我这么蒻肯定还是觉得有点难度的吧qwq)
不过我还是日常来补一下题解好了qwq

nowcoder(牛客网)OI测试赛3 解题报告
T1 数字权重(快速幂)
T2 毒瘤XOR(异或)(二进制)
T3 硬币游戏(贪心)
T4 粉樱花之恋(矩阵快速幂)(斐波那契数列)
T5 符合条件的整数(高精度)
T6 可爱即正义(字符串)
这个就是你把式子(sum_{i=2}^n(a_i-a_{i-1})=K)展开就是(a_n-a_1)嘛。。。所以就是水题一个qwq
但是要注意因为数据范围比较大,所以我们要有快速幂。而且注意不合法情况的特判qwq

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define mod 1000000007
using namespace std;
long long n,k,ans=1;
long long mul(long long x,long long y)
{
    if(!y) return 1;
    else
    {
        long long ans=mul(x,y>>1);
        if(y&1) return (ans*ans%mod)*(x%mod)%mod;
        else return ans*ans%mod;
    }
}
int main()
{
    scanf("%lld%lld",&n,&k);
    if(k>=10||n==1||k<=-10)
    {
        printf("0
");
        return 0;
    }
    if(k<0)
    {
        ans=mul(10,n-2)%mod;
        ans*=10-abs(k);
    }
    else
    {
        ans=mul(10,n-2)%mod;
        ans*=9-k;
    }
    printf("%lld
",ans%mod);
    return 0;
}

T2 毒瘤XOR(异或)(二进制)

nowcoder(牛客网)OI测试赛3 解题报告
T1 数字权重(快速幂)
T2 毒瘤XOR(异或)(二进制)
T3 硬币游戏(贪心)
T4 粉樱花之恋(矩阵快速幂)(斐波那契数列)
T5 符合条件的整数(高精度)
T6 可爱即正义(字符串)
这个我是把原数拆成二进制下的数(30位),然后进行贪心。我们要想输出最小的数,那么就在当前位下如果零的个数小于等于一的个数时让ans该位为0,将这个位置记录下来,然后在最后遍历一遍将其ans+=1<<记录的位数。
当然暴力地算不太行,所以我们考虑用前缀和qwq......
然后就60分光荣MLE了。。。我也不知道怎么回事。。。这里附上我的60分代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define MAXN 100010
using namespace std;
int n,q;
int a[MAXN],l[MAXN],r[MAXN],sum[MAXN][32],sum0[MAXN][32],sum1[MAXN][32];
long long ans;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        int cur=a[i],cnt=-1;
        while(cur)
        {
            sum[i][++cnt]=cur%2;
            cur>>=1;
        }
        for(int j=0;j<=31;j++)
        {
            if(sum[i][j]==1) sum1[i][j]=sum1[i-1][j]+1,sum0[i][j]=sum0[i-1][j];
            else  sum0[i][j]=sum0[i-1][j]+1,sum1[i][j]=sum1[i-1][j];
            //printf("sum0[%d][%d]=%d sum1[%d][%d]=%d
",i,j,sum0[i][j],i,j,sum1[i][j]);
        }
    }
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
        scanf("%d%d",&l[i],&r[i]);
    for(register int i=1;i<=q;i++)
    {
        ans=0;
        for(int j=0;j<=31;j++)
        {
            int cur1=sum0[r[i]][j]-sum0[l[i]-1][j];
            int cur2=sum1[r[i]][j]-sum1[l[i]-1][j];
            if(cur1<cur2||(cur1&&cur2&&cur1==cur2))
               ans+=1<<j;
        }
        long long ansans=2147483647-ans;
        printf("%lld
",ansans);
    }
     
    return 0;
}

然后发现可以用位运算来优化掉做法,所以。。。。AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define MAXN 100010
using namespace std;
int n;
int A[MAXN][31],sum[MAXN][31];
int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++){
        ll x;
        scanf("%lld",&x);
        for (int j=0;j<=30;j++) A[i][j]=(x&((ll)1<<(ll)j))!=0;
    }
    for (int i=1;i<=n;i++)
        for (int j=0;j<=30;j++)
            sum[i][j]=sum[i-1][j]+A[i][j];
    int m; scanf("%d",&m);
    while (m--){
        int l,r; scanf("%d %d",&l,&r);
        ll ans=0;
        for (int i=30;i>=0;i--){
            ans<<=1;
            int  num=sum[r][i]-sum[l-1][i];
            if (2*num<(r-l+1)) ans++;
        }
        printf("%lld
",ans);
    }
}

T3 硬币游戏(贪心)

nowcoder(牛客网)OI测试赛3 解题报告
T1 数字权重(快速幂)
T2 毒瘤XOR(异或)(二进制)
T3 硬币游戏(贪心)
T4 粉樱花之恋(矩阵快速幂)(斐波那契数列)
T5 符合条件的整数(高精度)
T6 可爱即正义(字符串)
这个题刚一看时是博弈论???但是再看就发现是简单的贪心。。。。qwq

题目中有一点描述不清,这里补充一下,该题就是clccle只能取第一列,sarlendy只能取第二列。(刚开始不知道这一点竟然判断起了奇偶性)

然后我们就贪心地先把都是1的标记到,然后clccle取他那一列是1的,然后sarlendy取他那一列是1的。然后判断大小就行了qwq

#include<iostream>
#include<cstdio>
#include<algorithm>
#define MAXN 2000010
using namespace std;
int n,sum1,score1,score2,done[MAXN];
char a[MAXN],b[MAXN];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=2*n;i++)
		cin>>a[i];
	for(int i=1;i<=2*n;i++)
	{
		cin>>b[i];
		if(a[i]=='U'&&b[i]=='U') sum1++,done[i]=1;
	}
	if(sum1<n)
	{
		for(int i=1;i<=n;i++)
		{
			if(a[i]=='U'&&done[i]==0) score1++,done[i]=1;
			if(b[i]=='U'&&done[i]==0) score2++,done[i]=1;
		}
		score1+=(sum1+1)/2;
		score2+=sum1-(sum1+1)/2;
		if(score1>score2)
			cout<<"clccle trl!"<<endl;
		else if(score1==score2||(score1>=n&&score2>=n))
			cout<<"orz sarlendy!"<<endl;
		else cout<<"sarlendy tql!"<<endl;
	}
	else
		cout<<"orz sarlendy!"<<endl;
	return 0;
}

T4 粉樱花之恋(矩阵快速幂)(斐波那契数列)

nowcoder(牛客网)OI测试赛3 解题报告
T1 数字权重(快速幂)
T2 毒瘤XOR(异或)(二进制)
T3 硬币游戏(贪心)
T4 粉樱花之恋(矩阵快速幂)(斐波那契数列)
T5 符合条件的整数(高精度)
T6 可爱即正义(字符串)
这个一看就是求斐波那契数列前n+1项和的模板题,但是怎么求斐波那契数列的前n项和呢?
这里我们需要用到一个结论:
我们用(f_i)来表示斐波那契数列的第(i)项,那么它的前n项和为(sum_{i=1}^nf_i=f_{n+2}-1)

下面我们来推导~~
先把左边的展开并+1:

(sum_{i=1}^nf_i+1)
(=f_1+f_2+f_3+...+f_n+1)
(=f_1+f_2+f_3+...+f_n+f_2)
(=(f_1+f_2)+f_2+f_3+...+f_n)
(=(f_2+f_3)+f_3+...+f_n)
(=f_n+f_{n+1})
(=f_{n+2})

得证:(sum_{i=1}^nfi=f_{n+2}-1)

那么其他的就超级简单啦!就是模板嘛。。。。。但是考虑到数据范围过大,所以我们要用(log(n))的矩阵快速幂来做
如果有矩阵快速幂不会的可以模板(戳我)
同机房还有dalao用三维矩阵做的,大概就是多开一个来储存和qwq(~~大家可以在我的友链区BLUESKY007 和 dreagonm找到ta们qwq)
下面放上我的代码qwq

#include<iostream>
#include<cstdio>
#include<algorithm>
#define mod 998244353
using namespace std;
struct Matrix
{
    long long a[3][3];
};
Matrix res,mul;
long long n,kk[3],ans[3];
Matrix multi(Matrix x,Matrix y)
{
    Matrix cur;
    for(int i=1;i<=2;i++)
       for(int j=1;j<=2;j++)
          cur.a[i][j]=0;
    for(int i=1;i<=2;i++)
        for(int j=1;j<=2;j++)
            for(int k=1;k<=2;k++)
                cur.a[i][j]=(cur.a[i][j]+x.a[i][k]*y.a[k][j])%mod;
    return cur;
}
void solve()
{
    for(int i=1;i<=2;i++)
       for(int j=1;j<=2;j++)
                ans[i]=(ans[i]+kk[j]*res.a[i][j])%mod;
    printf("%lld
",ans[2]-1);
}
int main(){
    for(int i=1;i<=2;i++)
       res.a[i][i]=1;
    for(int i=1;i<=2;i++)
       for(int j=1;j<=2;j++)
          mul.a[i][j]=1;
    mul.a[2][2]=0;
    kk[1]=kk[2]=1;
    scanf("%lld",&n);
    if(n==0)
    {
        printf("1
");
        return 0;
    }
    if(n==1)
    {
        printf("2
");
        return 0;
    }
    n=n+2;
    //n--;
    while(n)
    {
        if(n&1)
           res=multi(res,mul);
        n>>=1;
        mul=multi(mul,mul);
    }
    solve();
    return 0;
} 

T5 符合条件的整数(高精度)

nowcoder(牛客网)OI测试赛3 解题报告
T1 数字权重(快速幂)
T2 毒瘤XOR(异或)(二进制)
T3 硬币游戏(贪心)
T4 粉樱花之恋(矩阵快速幂)(斐波那契数列)
T5 符合条件的整数(高精度)
T6 可爱即正义(字符串)
这个。。。没写高精度,但是支持__int128啊!所以说。。。我们使用__int128类型就可以轻松水过这个题了。
注意我们找符合条件的要向上取整qwq
注意是左闭右开区间qwq,所以记得-1

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define ll __int128
inline ll f(int x) {
    ll a=(((ll)1)<<x)-1;
    ll ans=a/7;
    if(a%7!=0) ans++;
    return ans;
}
int main(){
    int n, m;
    scanf("%d%d",&n,&m);
    printf("%llu",(unsigned long long)(f(m)-f(n)));
    return 0;
}`

T6 可爱即正义(字符串)

nowcoder(牛客网)OI测试赛3 解题报告
T1 数字权重(快速幂)
T2 毒瘤XOR(异或)(二进制)
T3 硬币游戏(贪心)
T4 粉樱花之恋(矩阵快速幂)(斐波那契数列)
T5 符合条件的整数(高精度)
T6 可爱即正义(字符串)
这个题这个题。。。。再一次让我认识到了STL大法好!!看到别人手动匹配感觉好心累啊,还是用find()比较happy。
不过原先一直不知道find()还可以在指定范围内的字符串中查找字符串,今天算是知道了.....
比如说

    int k=s.find("abc");
    //这个是在字符串s中寻找abc,并且返回第一个位置(即a)
    int k2=s.find("abc",k+1);
    //这个就可以实现在字符串s中寻找abc存在的第二个位置
    //因为我们是从第k+1位开始寻找的

所以说代码就比较短吧qwq。。。。。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
string s;
int main()
{
	cin>>s;
	int ans=0;
	int pos=s.find("suqingnianloveskirito");
	if(pos==-1) {printf("Yes
%d %d
",1,2);return 0;}
	int pos2=s.find("suqingnianloveskirito",pos+1);
	if(pos2==-1) {printf("Yes
%d %d
",pos+1,pos+2);return 0;}
	int pos3=s.find("suqingnianloveskirito",pos2+1);
	if(pos3==-1) {printf("Yes
%d %d
",pos+1,pos2+2);return 0;}
	else printf("No
");
	return 0;
}