HDU 5381 The sum of gcd 离线处理+线段树
2015-08-24 10:56
405 查看
题意:给一组序列,多次询问,每次询问f(l,r)=∑ri=l∑rj=igcd(ai,ai+1....aj)
最初在赛场上看到这道题的时候,就想离线处理,但是怎么也不会。原因是当时并不知道这题的一个性质:从一个下标为i的数向后的连续区间[i,i+1]、[i,i+2]...[i,n]中不同的gcd(ai,ai+1,...,aj)最多只有log(ai)个。有了这个性质我们可以干啥呢,对于每一个左端点对答案的贡献就可以就能求出来;那么我们把询问按照左端点排序,从大到小回答询问,移动左端点时,用线段树来更新答案,遇到一组询问就把询问的答案记下,最后输出即可。
至于怎么更新答案,假设我们已经知道ai+1的每一个gcd对应的连续区间,那么我们用ai与这些gcd做一次求gcd,得到一些单调不增的gcd,那么我们把相同的gcd对应的区间合并,在线段树区间修改里加上对应的gcd就行。
时间复杂度O(nlog2n)
P.S.可以直接用树状数组维护,但是线段树无脑一些,所以直接用线段树做了。
14519477 | 2015-08-15 | 11:23:10 | Accepted | 5381 | 171MS | 2472K | 2659B | G++ | Kurama |
---|
#include <cstdio> #include <cstring> #include <cstdlib> #include <map> #include <algorithm> #define MAXN 10001 using namespace std; struct tree{int l,r;long long mark,val;}t[MAXN*4]; //线段树 struct query{int l,r,id;}q[MAXN]; //询问 bool cmp(const query &a,const query &b){return a.l>b.l;} int num[MAXN],g[MAXN],l[MAXN],r[MAXN]; long long ans[MAXN]; int res; void built(int left,int right,int pos) { t[pos].l=left,t[pos].r=right,t[pos].mark=0; if(left==right){t[pos].val=0;return;} int m=(left+right)>>1; built(left,m,2*pos); built(m+1,right,2*pos+1); t[pos].val=t[2*pos].val+t[2*pos+1].val; } //建树 void check(int pos) { if(t[pos].mark!=0){ t[pos].val+=(t[pos].r-t[pos].l+1)*t[pos].mark; if(t[pos].l!=t[pos].r){ t[pos*2+1].mark+=t[pos].mark; t[pos*2].mark+=t[pos].mark; } t[pos].mark=0; } } //下方标记 void update(int left,int right,int value,int pos) { check(pos); if(left==t[pos].l&&right==t[pos].r){t[pos].mark=value;return;} int m=(t[pos].l+t[pos].r)>>1; if(left>m)update(left,right,value,2*pos+1); else if(right<=m)update(left,right,value,2*pos); else{update(left,m,value,2*pos);update(m+1,right,value,2*pos+1);} check(2*pos+1);check(2*pos); t[pos].val=t[2*pos].val+t[2*pos+1].val; } //区间更新 long long ask(int left,int right,int pos) { check(pos); if(t[pos].l==left&&t[pos].r==right)return t[pos].val; int m=(t[pos].l+t[pos].r)>>1; if(left>m)return ask(left,right,2*pos+1); else if(right<=m)return ask(left,right,2*pos); return ask(left,m,2*pos)+ask(m+1,right,2*pos+1); } //区间询问 int _unique(int size){ int ct=0; for(int i=0;i<size;ct++,i++){ g[ct]=g[i];l[ct]=l[i];r[ct]=r[i]; while(g[i]==g[i+1]){ l[ct]=min(l[ct],l[i+1]); r[ct]=max(r[ct],r[i+1]); i++; } } g[ct]=0; return ct; } //合并相同区间 int main() { int t; scanf("%d",&t); while(t--){ int n,cnt=0; scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&num[i]); built(1,n,1); int qn; scanf("%d",&qn); for(int i=1;i<=qn;i++){ scanf("%d%d",&q[i].l,&q[i].r); q[i].id=i; } sort(q+1,q+qn+1,cmp); //记下询问并排序 memset(g,0,sizeof g); for(int i=n,k=1;i>=1;i--){ //更新左端点信息,cnt为不同gcd的区间数 for(int j=0;j<cnt;j++){ int x=__gcd(num[i],g[j]); g[j]=x; } g[cnt]=num[i];l[cnt]=r[cnt]=i; ++cnt; cnt=_unique(cnt); for(int j=0;j<cnt;j++)update(l[j],r[j],g[j],1); //对左端点的连续区间在线段树里加上贡献 while(q[k].l==i){ ans[q[k].id]=ask(q[k].l,q[k].r,1); k++; } //回答对于这个端点的询问 } for(int i=1;i<=qn;i++)printf("%lld\n",ans[i]); } return 0; }
相关文章推荐
- Hadoop 离线安装 CDH5.1 第一章:离线环境部署
- Android环境离线安装--极速版
- Windows2008+MyEclipse10+Android开发环境搭配
- gcd
- OC多线程
- 多线程应该知道的那几件事 GCD NSThread NSOperation
- 多线程编程4 - GCD
- 你们一句有时侯顶我们设计十句
- 谷歌眼镜开发指南之概述
- 多线程学习资源
- block && Grand Central Dispatch
- GCD使用指南
- 迅雷离线工具 小众雷友 测试版
- 线段树题集
- eclipse离线安装【link方式安装】jetty插件
- 2分钟学会GCD
- Problem 1612 Hero's gcd
- 计算两个数的最大公约数 gcd(a,b)
- 模运算相关数论知识
- hdu1754