[YY题]HDOJ5288 OO’s Sequence
题意:求这个式子 $\sum \limits_{i=1}^{n} \sum \limits_{j=1}^{m} f(i, j) mod (10^9 + 7)$ 的值
就是对每个区间[i, j]枚举区间中的每个数$a_i$到$a_j$, 判断这个$a$是否对[i, j]这个区间内所有数取模都不等于0, 若是,则这个区间满足条件
问有多少个满足条件的区间
比如案例是这样跑的
int ans=0; for(int i=1;i<=5;i++) for(int j=i;j<=5;j++) { for(int k=i;k<=j;k++) // 注意要枚举[i, j]中的每个数 { bool flag=0; for(int l=i;l<=j;l++) if(k!=l && k%l==0) flag=1; if(!flag) // 对区间内所有数取模都不等于0 ans++; } }
跟省赛某题很像, 计算每个数a[i]对ans的贡献
比如对于案例 1 2 3 4 5
1这个数字对于答案的贡献是{1}, {1, 2}, {1, 2, 3}, {1, 2, 3, 4}, {1, 2, 3, 4, 5} 这5个区间
2 这个数字对于答案的贡献是{2}, {2, 3}, {2, 3, 4}, {2, 3, 4, 5} 这4个区间 ( {1, 2}区间不满足,因为2%1==0 )
... ...
来看4 这个数
它往左取区间 {4}, {3, 4} 当取到2的时候, 发现4%2==0了,那么就不必再往左了(对于连续的区间, 再往左则必定会经过2,那么该区间就不合法了)
同理,可以想到往右取, 当找到一个数被它取模等于0, 那么就不必再往右了
好了,现在对于一个数a[i], 它左边 到 不合法的数(被它取模等于0)为止 之间有x个数, 右边到不合法的数为止 有y个数
那么a[i]这个数对答案的贡献就是(x+1)*(y+1)
为什么呢?
因为是连续的区间, 所以这个区间的左端点可以取a[i]左边0个数、1个数、2个数... ...x个数;右边0个数、1个数、2个数... ...y个数
左边有(x+1)种取法,右边有(y+1)种, 相乘就是总取法数
那么我们只要找到离a[i]最近的一左一右两个不合法数的位置$l$和$r$, 那么$(i-l)*(r-i)$ 就是a[i]的贡献($i-l-1$就是上面所讲的x)
之后只要遍历每个a, 将每个a的贡献累加起来即是最后答案。
那么现在问题就转化成了如何求一个 离它最近的 能被它整除的数 的位置
我们是这样做的:
开个数组将每个a[i]的倍数都记为i (比如 a[0]=2, 那么就将2、4、6... ...10000 都记下0号位置;a[x]=y, 就将y、2y、3y... ... 都记下x位置)
就跟筛因子一样 (因为1比较特殊,会退化到$n^2$, 因此特殊处理) 复杂度为O(NlogN) (N为10000, 因为数最大为10000)
for(int i=0;i<n;i++) { if(a[i]==1) one.push_back(i); else for(int j=a[i];j<=10000;j+=a[i]) b[j].push_back(i); }
因为是按顺序遍历了a数组, 所以记下的位置(比如2 2 3 6 12 对于12记下的是0 1 2 3 4 )一定是递增的
那么就可以二分来寻找离$i$最近的位置
p.s. lower_bound 找的是大于等于x的数位置
upper_bound找的是大于x的数的位置
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 const LL mod=1e9+7; 2 int a[100005]; 3 vector<int> b[10005]; 4 vector<int> one; 5 int read() 6 { 7 char ch=' '; 8 int ans=0; 9 while(ch<'0' || ch>'9') 10 ch=getchar(); 11 while(ch<='9' && ch>='0') 12 { 13 ans=ans*10+ch-'0'; 14 ch=getchar(); 15 } 16 return ans; 17 } 18 19 int main() 20 { 21 // freopen("1001.in", "r", stdin); 22 // freopen("out.txt", "w", stdout); 23 int n; 24 while(~scanf("%d", &n)) 25 { 26 for(int i=0;i<n;i++)a[i]=read(); 27 // scanf("%d", &a[i]); 28 for(int i=0;i<=10000;i++) 29 { 30 b[i].clear(); 31 b[i].push_back(-1); 32 } 33 one.clear(); 34 one.push_back(-1); 35 for(int i=0;i<n;i++) 36 { 37 if(a[i]==1) 38 one.push_back(i); 39 else 40 for(int j=a[i];j<=10000;j+=a[i]) 41 b[j].push_back(i); 42 } 43 for(int i=0;i<=10000;i++) 44 b[i].push_back(n); 45 one.push_back(n); 46 LL ans=0; 47 for(int i=0;i<n;i++) 48 { 49 int p1=lower_bound(b[a[i]].begin(), b[a[i]].end(), i)-b[a[i]].begin()-1; 50 int p2=lower_bound(one.begin(), one.end(), i)-one.begin()-1; 51 int l=max(b[a[i]][p1], one[p2]); 52 p1=upper_bound(b[a[i]].begin(), b[a[i]].end(), i)-b[a[i]].begin(); 53 p2=upper_bound(one.begin(), one.end(), i)-one.begin(); 54 int r=min(b[a[i]][p1], one[p2]); 55 // printf("%d %d\n", l, r); 56 ans=(ans+((i-l)*(r-i))%mod)%mod; 57 } 58 printf("%I64d\n", ans%mod); 59 } 60 return 0; 61 }