HDU 4913 Least common multiple
题目:Least common multiple
链接:http://acm.hdu.edu.cn/showproblem.php?pid=4913
题意:有一个集合s,包含x1,x2,...,xn,有xi=2^ai * 3^bi,然后给你a数组和b数组,求s所有子集合的最小公倍数之和。比如S={18,12,18},那么有{18},{12},{18},{18,12},{18,18},{12,18},{18,12,18},所以答案是174。
思路:
1. 最小公倍数,因为xi只包含两个质因子2、3,那么子集合的最小公倍数其实就是2^max(a[]) * 3^max(b[])。max(a[])就是子集合相关的a数组集合中最大的ai。
2. 先简化题目,如果题目中的xi = 2^ai,那么可以对x数组进行从小到大排序。那么 lcm(x0,x1,...,xi)=xi,又 i 前面包含xi的子集合数量为2^(i-1)(其实就是xj(j<i)放入集合为1,不放入集合为0),那么最后的答案为 ∑ (2^ i-1 ) * xi,也就是ans[i]=ans[i-1]+2^(i-1)*2^(ai)
3. 回归题目,题目中多了b数组,那么我们可以按b排序,令 x= i 前面 a的值小于ai的数量,令p1、p2、...、pk为i 前面 a的值比ai大的 位置。那么ans[i] = ans[i-1] + ( 2^x * 2^ai + 2^x * 2^ap1 + 2^(x+1) * 2^ap2 + ... + 2^(x+k-1) * 2^apk ) * 3^bi。
3中的递推公式可以如下理解:
推到i 就表示i 必选,那么i 前面最小公倍数为2^ai * 3^bi 的有 2^x 个,就是 x 个 a的值比ai小的xj 选与不选的问题了。比ai大的不选(因为选了的话,最小公倍数就不是xi了)
然后xp1必选,也就是 第一个a的值比ai大的 xj必选,又 xi 必选,剩下x个比ai小的可选可不选,有2^x种情况,这2^x种情况的最小公倍数为2^ap1 * 3^bi。
再接着xp2必选(注意:xi 还是必选,但xp1可选可不选),有2^(x+1)种情况,他们的最小公倍数为2^ap2 * 3^bi。
。。。
上面的递推公式只是告知ans[i]的算法,当然不能直接照样计算,因为即使你可以很容易得到 i 前面a值比ai大的位置,最坏情况下(b递增,a递减),时间复杂度也高达O(n*n)。
现在我们可以维护2^x * 2^aj(其实就是维护那个x值),我们建一个线段树(离散型的),孩子结点(i,i)对应a值排名为i 的xj(x数组是按b排序的),线段树的结点包含sum、cnt和mulCnt三个属性,当遍历(从1-n)到j 时,xj对应的a值排名i的线段树结点cnt置1,sum置为2^x * 2^aj,然后向上更新,现在如果问x值,就可以用线段树很快地得出来。就是1-i 中cnt已经是1的数量。而2^x * 2^ap1 + 2^(x+1) * 2^ap2 + ... + 2^(x+k-1) * 2^apk可以通过维护得到,就是说每次计算完,从i+1到n的已经置为1(就是前面已经出现过的)的sum值乘2(因为比他们小的又多了一个aj,就是x+1)。
AC代码:
1 #include<stdio.h> 2 #include<string.h> 3 #include<math.h> 4 #include<algorithm> 5 #define Mod 1000000007 6 using namespace std; 7 typedef long long LL; 8 9 LL qpow(LL a,LL b) 10 { 11 LL ret=1; 12 while(b) 13 { 14 if(b&1) ret=ret*a%Mod; 15 a=a*a%Mod; 16 b>>=1; 17 } 18 return ret; 19 } 20 21 struct X 22 { 23 int id; 24 int a,b; 25 int ra; //a的排名 26 }; 27 bool cmp1(X a,X b) //根据a排序 28 { 29 return a.a<b.a; 30 } 31 bool cmp2(X a,X b) //根据b排序 32 { 33 return a.b<b.b; 34 } 35 36 X x[100010]; 37 38 struct Node 39 { 40 LL sum,cnt; 41 LL mulCnt; //该区间乘了多少个2 42 int l,r; 43 int mid() 44 { 45 return (l+r)/2; 46 } 47 }; 48 Node v[400040]; 49 void build(int l,int r,int rt) 50 { 51 v[rt].sum=v[rt].cnt=0; 52 v[rt].mulCnt=0; 53 v[rt].l=l; 54 v[rt].r=r; 55 if(l==r) return ; 56 build(l,v[rt].mid(),rt<<1); 57 build(v[rt].mid()+1,r,rt<<1|1); 58 } 59 60 void push_down(int rt,bool flag) 61 { 62 if(v[rt].l==v[rt].r) 63 { 64 v[rt].mulCnt=0; 65 return ; 66 } 67 v[rt<<1].mulCnt+=v[rt].mulCnt; 68 v[rt<<1|1].mulCnt+=v[rt].mulCnt; 69 if(flag==0) 70 { 71 v[rt<<1].sum=v[rt<<1].sum*qpow(2,v[rt<<1].mulCnt)%Mod; 72 push_down(rt<<1,1); 73 v[rt<<1|1].sum=v[rt<<1|1].sum*qpow(2,v[rt<<1|1].mulCnt)%Mod; 74 push_down(rt<<1|1,1); 75 } 76 v[rt].mulCnt=0; 77 } 78 79 LL look_cnt(int l,int r,int rt) 80 { 81 if(l==v[rt].l&&r==v[rt].r) return v[rt].cnt; 82 int mid=v[rt].mid(); 83 if(l>mid) return look_cnt(l,r,rt<<1|1); 84 else if(r<=mid) return look_cnt(l,r,rt<<1); 85 else return look_cnt(l,mid,rt<<1)+look_cnt(mid+1,r,rt<<1|1); 86 } 87 88 LL look_sum(int l,int r,int rt) 89 { 90 if(l==v[rt].l&&r==v[rt].r) 91 { 92 v[rt].sum=v[rt].sum*qpow(2LL,v[rt].mulCnt)%Mod; 93 push_down(rt,0); 94 return v[rt].sum; 95 } 96 push_down(rt,0); 97 int mid=v[rt].mid(); 98 LL ret; 99 if(l>mid) ret=look_sum(l,r,rt<<1|1); 100 else if(r<=mid) ret=look_sum(l,r,rt<<1); 101 else ret=(look_sum(l,mid,rt<<1)+look_sum(mid+1,r,rt<<1|1))%Mod; 102 v[rt].sum=(v[rt<<1].sum+v[rt<<1|1].sum)%Mod; 103 return ret; 104 } 105 106 void update(int ra,int a,int rt) 107 { 108 if(v[rt].l==v[rt].r) 109 { 110 v[rt].cnt=1; 111 LL x = ra==0 ? 0 :look_cnt(0,ra-1,1); //x 就是在a的前面(按b排序后)排名比a小的数量 112 v[rt].sum=qpow(2,x+a); 113 v[rt].mulCnt=0; 114 return ; 115 } 116 push_down(rt,0); 117 if(ra<=v[rt].mid()) 118 update(ra,a,rt<<1); 119 else update(ra,a,rt<<1|1); 120 v[rt].cnt=v[rt<<1].cnt+v[rt<<1|1].cnt; 121 v[rt].sum=(v[rt<<1].sum+v[rt<<1|1].sum)%Mod; 122 } 123 124 void mul(int l,int r,int rt) 125 { 126 if(v[rt].l==l&&v[rt].r==r) 127 { 128 v[rt].mulCnt++; 129 v[rt].sum=v[rt].sum*qpow(2LL,v[rt].mulCnt)%Mod; 130 push_down(rt,0); 131 return ; 132 } 133 push_down(rt,0); 134 if(r<=v[rt].mid()) mul(l,r,rt<<1); 135 else if(l>v[rt].mid()) mul(l,r,rt<<1|1); 136 else 137 { 138 mul(l,v[rt].mid(),rt<<1); 139 mul(v[rt].mid()+1,r,rt<<1|1); 140 } 141 v[rt].sum=(v[rt<<1].sum+v[rt<<1|1].sum)%Mod; 142 } 143 144 int main() 145 { 146 int n; 147 while(scanf("%d",&n)!=EOF) 148 { 149 build(0,n-1,1); 150 for(int i=0;i<n;i++) 151 { 152 scanf("%d%d",&x[i].a,&x[i].b); 153 x[i].id=i; 154 } 155 sort(x,x+n,cmp1); 156 for(int i=0;i<n;i++) x[i].ra=i; 157 sort(x,x+n,cmp2); 158 LL ans=0; 159 for(int i=0;i<n;i++) 160 { 161 update(x[i].ra,x[i].a,1); 162 ans = (ans + look_sum(x[i].ra,n-1,1)*qpow(3LL,x[i].b))%Mod; 163 if(x[i].ra!=n-1) mul(x[i].ra+1,n-1,1); 164 } 165 printf("%I64d\n",ans); 166 } 167 return 0; 168 }