T1 [模拟赛20221101] 子集
偶数很好想,奇数先按偶数的搞了中间留三列找规律(乱搞)
T2 最大公约数
gcd的递减是log的段数,所以可以二分每一段统计答案,st表比线段树要快很多
也可以不用数据结构,分成log段,直接从左右端点往里统计答案
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n,q,a[N],l,r;
map<int,int>cnt;
inline int gcd(int a,int b){
if(b==0){
return a;
}else{
return gcd(b,a%b);
}
}
int _log[N],f[N][20];
void init(){
for(int i=1;i<=n;i++){
_log[i]=log2(i);
}
for(int i=1;i<=n;i++){
f[i][0]=a[i];
}
for(int j=1;j<=_log[n];j++){
for(int i=1;i<=n-(1<<j)+1;i++){
f[i][j]=gcd(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
}
}
int ask(int x,int y){
int k=_log[y-x+1];
return gcd(f[x][k],f[y-(1<<k)+1][k]);
}
int find(int fr,int l,int r,int x){
while(l<r){
int mid=(l+r+1)>>1;
if(ask(fr,mid)==x){
l=mid;
}else{
r=mid-1;
}
}
return l;
}
void solve(){
for(int i=1;i<=n;i++){
int j=i;
while(j<=n){
int jj=find(i,j,n,ask(i,j));
cnt[ask(i,j)]+=jj-j+1;
j=jj+1;
}
}
}
signed main(){
scanf("%lld%lld",&n,&q);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
init();
solve();
int l,r;
for(int i=1;i<=q;i++){
scanf("%lld%lld",&l,&r);
int ans=ask(l,r);
printf("%lld %lld\n",ans,cnt[ans]);
}
return 0;
}
T3 夺宝
题意不好理解,它的价值实际上是suma*sumb,直接暴搜肯定是不行的,我们先设状态:
f[x][y][a]=b 在a相同的情况下b肯定越大越好,但这只能通过第三档数据,我们可以暴力把每个状态都存下来,最后合并时用归并排序的思想,只保存阶梯状的点
T4 [模拟赛20221103] Zbox的刷题I
期望dp,表示恰好i轮AK的概率,那么求期望的话,用它推式子(还在想)
T5 各行其道
换根dp