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.快乐风男
题意:
解法:
code:
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;
}