牛客小白月賽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;
}

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