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;
}