牛客小白月赛20

牛客小白月赛20


A.斐波那契

题意:

给定n,计算斐波那契数列前n项的平方和,对1e9+7取模
n<=1e18

思路:

斐波那契数列的前n项平方和=f(n)*f(n+1),推导过程在此
因此利用矩阵快速幂计算出两项斐波那契数,相乘就是答案

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int n=2;
const int mod=1e9+7;
struct Node{
    int a[n][n];
};
Node mul(Node a,Node b){
    Node ans;
    for(int i=0;i<n;i++)for(int j=0;j<n;j++)ans.a[i][j]=0;
    for(int k=0;k<n;k++)
        for(int i=0;i<n;i++)if(a.a[i][k])
            for(int j=0;j<n;j++)if(b.a[k][j])
                ans.a[i][j]=(ans.a[i][j]+a.a[i][k]*b.a[k][j]%mod)%mod;
    return ans;
}
Node PPow(Node a,int b){
    Node ans;
    for(int i=0;i<n;i++)for(int j=0;j<n;j++)ans.a[i][j]=(i==j);
    while(b){
        if(b&1)ans=mul(ans,a);
        a=mul(a,a);
        b>>=1;
    }
    return ans;
}
signed main(){
    int x;
    cin>>x;
    Node base;
    int c[n][n]={1,1,1,0};
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            base.a[i][j]=c[i][j];
        }
    }
    Node ans=PPow(base,x);
    int a=ans.a[0][0];
    int b=ans.a[0][1];
    int res=a*b%mod;
    cout<<res<<endl;
    return 0;
}

C.球的表面积


E.区区区间

题意:

给长度为n的序列,m次操作
两种操作:
1.(l,r,k) 把区间[l,r]变为k,k+1,k+2…k+r-l
2.(l,r) 查询区间[l,r]的和

思路:

操作2可以用线段树的区间求和
操作1可以用线段树的区间覆盖
但是操作1的修改,区间中每个数是不同的
发现区间中连续的数是等差数列,因此知道左端点的值就能推出区间内所有点的值
对于线段树区间节点node,用laz标记node节点区间左端点的值
利用node节点的laz,可以推出node左节点的laz和右节点的laz
因为是等差数列,node节点的区间和可以直接利用公式计算出来

线段树维护区间和和laz标记即可
然后就是注意细节的处理(debug好累)

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=2e5+5;
int a[maxm<<2];
int laz[maxm<<2];
void pushup(int node){//
    a[node]=a[node*2]+a[node*2+1];
}
void pushdown(int node,int len1,int len2){
    laz[node*2]=laz[node];
    laz[node*2+1]=laz[node]+len1;
    a[node*2]=len1*(laz[node*2]+laz[node*2]+len1-1)/2;
    a[node*2+1]=len2*(laz[node*2+1]+laz[node*2+1]+len2-1)/2;
    laz[node]=0;
}
void build(int l,int r,int node){//
    if(l==r){
        cin>>a[node];
        return ;
    }
    int mid=(l+r)/2;
    build(l,mid,node*2);
    build(mid+1,r,node*2+1);
    pushup(node);
}
void update(int st,int ed,int val,int l,int r,int node){
    if(st<=l&&ed>=r){
        laz[node]=val+(l-st);
        a[node]=(r-l+1)*(laz[node]+laz[node]+r-l)/2;
        return ;
    }
    int mid=(l+r)/2;
    if(laz[node])pushdown(node,mid-l+1,r-mid);
    if(st<=mid)update(st,ed,val,l,mid,node*2);
    if(ed>mid)update(st,ed,val,mid+1,r,node*2+1);
    pushup(node);
}
int ask(int st,int ed,int l,int r,int node){
    if(st<=l&&ed>=r)return a[node];
    int mid=(l+r)/2;
    if(laz[node])pushdown(node,mid-l+1,r-mid);
    int ans=0;
    if(st<=mid)ans+=ask(st,ed,l,mid,node*2);
    if(ed>mid)ans+=ask(st,ed,mid+1,r,node*2+1);
    pushup(node);
    return ans;
}
signed main(){
    int n,m;
    cin>>n>>m;
    build(1,n,1);
    for(int i=1;i<=m;i++){
        int d,l,r;
        cin>>d>>l>>r;
        if(d==1){
            int k;
            cin>>k;
            update(l,r,k,1,n,1);
        }else{
            int ans=ask(l,r,1,n,1);
            cout<<ans<<endl;
        }
    }
    return 0;
}

G.快乐风男


J.dh的帽子

题意:

给L,R
问有多少组a,b,c,满足a⊕b⊕c==a∣b∣c,(L<=a,b,c<=R)
L,R<=1e5

思路:

数位dp
一般数位dp有上界用来剪枝。这题还需要下界,也用于剪枝。
首先改为二进制枚举数位。递归的时候每一个数除了limit判断上界,还需要一个ismin判断下界。
三重for循环枚举三个数字当前数位的情况取0或者1,每个数位只能在上下界内取。
每次判断当前枚举的数位是否满足a⊕b⊕c==a∣b∣c,如果满足就继续下一位。不满足就跳出。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=35;
int num1[maxm],num2[maxm];
int d[maxm][2][2][2][2][2][2];//d数组需要longlong
int dfs(int len,int l1,int l2,int l3,int m1,int m2,int m3){
    if(len==-1)return 1;
    if(l1+l2+l3==0&&d[len][l1][l2][l3][m1][m2][m3]!=-1){
        return d[len][l1][l2][l3][m1][m2][m3];
    }
    int ma1=l1?num1[len]:1;
    int ma2=l2?num1[len]:1;
    int ma3=l3?num1[len]:1;
    int mi1=m1?num2[len]:0;
    int mi2=m2?num2[len]:0;
    int mi3=m3?num2[len]:0;
    int ans=0;
    for(int i=mi1;i<=ma1;i++){
        for(int j=mi2;j<=ma2;j++){
            for(int k=mi3;k<=ma3;k++){
                if((i^j^k)!=(i|j|k))continue;
                ans+=dfs(len-1,l1&&i==ma1,l2&&j==ma2,l3&&k==ma3,
                         m1&&i==mi1,m2&&j==mi2,m3&&k==mi3);
            }
        }
    }
    if(l1+l2+l3==0)d[len][l1][l2][l3][m1][m2][m3]=ans;
    return ans;
}
int solve(int l,int r){
    for(int i=0;i<=30;i++){
        num1[i]=(r>>i&1);//上界
        num2[i]=(l>>i&1);//下界
    }
    return dfs(30,1,1,1,1,1,1);
}
signed main(){
    memset(d,-1,sizeof d);
    int l,r;
    cin>>l>>r;
    int ans=solve(l,r);
    cout<<ans<<endl;
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章