题目链接:http://codeforces.com/contest/1062/problem/C
题意:你给一个长度为n的01串,表示该位置的初始值。每当我们选择某个位置的时,我们会获得当前位置的值然后删除该位置,并且其他没删除的位置会加上当前位置的值。然后给你q组询问,每组询问两个数,l,r,表示我们要查询的区间。 q查询次数1e5,n长度1e5。
题解:
先解释一波样例
4 2
1011
1 4
3 4
4表示四个数,2表示两次询问
1011表示初始位置的值。然后
1 4 表示查询区间【1,4】.我们先选择第一个位置x122,获得1的价值。然后选择第三个位置x3x4,获得价值2,然后选择第四个位置,x7xx,获得价值4。然后选择位置2xxxx。获得价值7,总计获得14点。
我们可以发现,如果要获得价值最大,我们每次选择剩余的最大值即可。模拟会超时QAQ,那我们想一下其他方法。
然后我们会发现,初始为1的变化是一定的。
1 2 4 8 16 32……
长度为1的个数
然后初始0为的变化为,初始为1的变化完得到的价值ans
ans,ans*2,ans*4,ans*8……
长度为0的个数
等比序列QAQ!!!!
等比序列求和公式 a1*(1-q^n)/(1-q)。
思考一下1个数如何求,肯定不能暴力,那么就前缀和+后缀和求一下。
幂运算用快速幂把。
会爆ing,记得开long long
到此这道题就OK了。
代码如下:
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
const int mod = 1e9 + 7;
char a[maxn];
long long pre[maxn];
long long suf[maxn];
long long pw(long long x, long long y, long long p) {
if (!y) {
return 1;
}
long long res = pw((x*x)%mod,y/2,p);
if (y & 1) {
res = res * x % p;
}
return res;
}
long long Sum(long long a1,long long q,long long cnt){
return a1*(1-(long long)pw(q,cnt,mod))/(1-q);
}
int main()
{
long long n,q;
scanf("%lld%lld",&n,&q);
scanf("%s",a+1);
pre[1] = a[1] - '0';
for(long long i = 2 ; i <= n ; i++ ){
pre[i] = a[i] - '0' + pre[i - 1];
}
suf[n] = a[n] - '0';
for(long long i = n - 1 ; i >= 1 ; i --){
suf[i] = a[i] - '0' + suf[i+1];
}
while(q--){
long long l,r;
scanf("%lld%lld",&l,&r);
long long len = r - l + 1;
long long res = pre[n] - pre[l - 1];
res -= suf[r+1];
long long onenum = res;
long long zeronum = len - res;
long long ans = 0;
ans += Sum(1,2,onenum);
if(zeronum){
ans += Sum(ans,2,zeronum);
}
ans %= mod;
cout << ans << endl;
}
return 0;
}