hdu5286 wyh2000 and sequence
2016-07-08 20:30
471 查看
题目大意:给一段长度为N的序列,每次询问l-r(l和r和上一次询问的答案有关)内 不同的数的 出现次数的次方 的和。
大体思路就是,把n个数分成sqrt(n)块,每块sqrt(n)个数,然后求出任意两块i到j(包含第i块和第j块)的ans,对于每次询问l和r,找到刚好包含l和r的一段连续的块,因为我们已经知道了这段连续块的答案,所以只要再去掉两段多出来的一部分,就好了。
为了得到任意两块间的答案G[i][j],我们可以枚举每个块的起点,然后for到最后,暴力一边,就好了,
对于多出来的一部分,我们只要知道这一部分里的数,在包含它的连续块中已经出现了几次,然后就可以很容易地更新答案了。于是我们可以先预处理出一个后缀和cnt[i][j]:表示从第i块开始,第j小的数出现了多少次,然后,对于每次询问q,我们 只要 只要 只要 (后缀和减一下)找到这一部分里的数在连续的块中出现了次数就好了,别的数就算了,都找的话,肯定超时。
之前还要预处理各种数次方的结果,还要排序,去重。
#include <bits/stdc++.h>
using namespace std;
const int MOD=1000000007;
const int maxn=5e4+5;
int cnt[240][maxn];//cnt[i][j]:从第i块开始到最后第j小的数出现的次数
long long A[maxn],a[maxn],num[maxn],vis[maxn];
int pos[maxn];//pos[i]:在A中下标为i的数是第几小
long long G[240][240];//G[i][j]:块i~j的答案
vector<long long>v[maxn];//v[i][j]:第i小的数的j次方
int n,m,unit,len,l,r;
int solve(int l,int r)
{
int L=l/unit,R=r/unit;//获取所在的块
long long ans=G[L][R];
int LL=L*unit,RR=min(n-1,(R+1)*unit-1);//获取具体下标的的范围,且LL~RR肯定包含l~r的
for(int i=LL;i<l;i++) num[pos[i]]=cnt[L][pos[i]]-cnt[R+1][pos[i]];
for(int i=r+1;i<=RR;i++) num[pos[i]]=cnt[L][pos[i]]-cnt[R+1][pos[i]];
while(LL<l)
{
ans-=v[pos[LL]][num[pos[LL]]];
num[pos[LL]]--;
ans+=v[pos[LL]][num[pos[LL]]];
LL++;
}
while(RR>r)
{
ans-=v[pos[RR]][num[pos[RR]]];
num[pos[RR]]--;
ans+=v[pos[RR]][num[pos[RR]]];
RR--;
}
return (ans%MOD+MOD)%MOD;
}
int main()
{
int i,j,k,T;
scanf("%d",&T);
while(T--)
{
int la=0;
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)
{
scanf("%lld",&A[i]);
a[i]=A[i];
}
sort(a,a+n);
len=unique(a,a+n)-a;
for(i=0;i<len;i++) v[i].clear(),v[i].push_back(0),vis[i]=0;
for(i=0;i<n;i++)
{
pos[i]=lower_bound(a,a+len,A[i])-a;//确定A[i]是第几小
vis[pos[i]]++;
}
for(i=0;i<len;i++)
{
long long p=1;
for(j=0;j<vis[i];j++)
{
p=p*a[i]%MOD;
v[i].push_back(p);
}
}
unit=sqrt(n);
memset(cnt,0,sizeof(cnt));
for(i=0;i*unit<n;i++)
{
long long ans=0;
memset(num,0,sizeof(num));
for(j=i*unit;j<n;j++)
{
cnt[i][pos[j]]++;
ans-=v[pos[j]][num[pos[j]]];
num[pos[j]]++;
ans+=v[pos[j]][num[pos[j]]];
if((j+1)%unit==0 || j==n-1)
G[i][j/unit]=(ans%MOD+MOD)%MOD;
}
}
while(m--)
{
int L,R;
scanf("%d%d",&L,&R);
l=min((L^la)%n,(R^la)%n);
r=max((L^la)%n,(R^la)%n);
la=solve(l,r);
printf("%d\n",la);
}
}
return 0;
}
大体思路就是,把n个数分成sqrt(n)块,每块sqrt(n)个数,然后求出任意两块i到j(包含第i块和第j块)的ans,对于每次询问l和r,找到刚好包含l和r的一段连续的块,因为我们已经知道了这段连续块的答案,所以只要再去掉两段多出来的一部分,就好了。
为了得到任意两块间的答案G[i][j],我们可以枚举每个块的起点,然后for到最后,暴力一边,就好了,
对于多出来的一部分,我们只要知道这一部分里的数,在包含它的连续块中已经出现了几次,然后就可以很容易地更新答案了。于是我们可以先预处理出一个后缀和cnt[i][j]:表示从第i块开始,第j小的数出现了多少次,然后,对于每次询问q,我们 只要 只要 只要 (后缀和减一下)找到这一部分里的数在连续的块中出现了次数就好了,别的数就算了,都找的话,肯定超时。
之前还要预处理各种数次方的结果,还要排序,去重。
#include <bits/stdc++.h>
using namespace std;
const int MOD=1000000007;
const int maxn=5e4+5;
int cnt[240][maxn];//cnt[i][j]:从第i块开始到最后第j小的数出现的次数
long long A[maxn],a[maxn],num[maxn],vis[maxn];
int pos[maxn];//pos[i]:在A中下标为i的数是第几小
long long G[240][240];//G[i][j]:块i~j的答案
vector<long long>v[maxn];//v[i][j]:第i小的数的j次方
int n,m,unit,len,l,r;
int solve(int l,int r)
{
int L=l/unit,R=r/unit;//获取所在的块
long long ans=G[L][R];
int LL=L*unit,RR=min(n-1,(R+1)*unit-1);//获取具体下标的的范围,且LL~RR肯定包含l~r的
for(int i=LL;i<l;i++) num[pos[i]]=cnt[L][pos[i]]-cnt[R+1][pos[i]];
for(int i=r+1;i<=RR;i++) num[pos[i]]=cnt[L][pos[i]]-cnt[R+1][pos[i]];
while(LL<l)
{
ans-=v[pos[LL]][num[pos[LL]]];
num[pos[LL]]--;
ans+=v[pos[LL]][num[pos[LL]]];
LL++;
}
while(RR>r)
{
ans-=v[pos[RR]][num[pos[RR]]];
num[pos[RR]]--;
ans+=v[pos[RR]][num[pos[RR]]];
RR--;
}
return (ans%MOD+MOD)%MOD;
}
int main()
{
int i,j,k,T;
scanf("%d",&T);
while(T--)
{
int la=0;
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)
{
scanf("%lld",&A[i]);
a[i]=A[i];
}
sort(a,a+n);
len=unique(a,a+n)-a;
for(i=0;i<len;i++) v[i].clear(),v[i].push_back(0),vis[i]=0;
for(i=0;i<n;i++)
{
pos[i]=lower_bound(a,a+len,A[i])-a;//确定A[i]是第几小
vis[pos[i]]++;
}
for(i=0;i<len;i++)
{
long long p=1;
for(j=0;j<vis[i];j++)
{
p=p*a[i]%MOD;
v[i].push_back(p);
}
}
unit=sqrt(n);
memset(cnt,0,sizeof(cnt));
for(i=0;i*unit<n;i++)
{
long long ans=0;
memset(num,0,sizeof(num));
for(j=i*unit;j<n;j++)
{
cnt[i][pos[j]]++;
ans-=v[pos[j]][num[pos[j]]];
num[pos[j]]++;
ans+=v[pos[j]][num[pos[j]]];
if((j+1)%unit==0 || j==n-1)
G[i][j/unit]=(ans%MOD+MOD)%MOD;
}
}
while(m--)
{
int L,R;
scanf("%d%d",&L,&R);
l=min((L^la)%n,(R^la)%n);
r=max((L^la)%n,(R^la)%n);
la=solve(l,r);
printf("%d\n",la);
}
}
return 0;
}
相关文章推荐
- SOAPUI使用教程-REST请求工作
- android studio关于build/outputs/apk/中apk或arr或jar无法删除的解决方法
- hdu 5063 Operation the Sequence(思路)
- UIweib的简单实用
- 将UIview描画成虚线等.
- 去掉UITableView多余的空白行分割线
- js 用defaultValue 模拟重置键 reset
- QOS-4 Queue队列机制-2 CBWFQ and LLQ
- QOS-3 Queue 队列机制
- UVA - 424 Integer Inquiry
- JUC源码分析19-队列-PriorityBlockingQueue
- Android UI性能问题探讨
- iOS UITextView 纯英文排版换行出错的解决方案
- dump View Hierarchy for UI automator
- UITableView的内容视图在有无导航栏下的区别
- iOS UiCollectionViewtroller 间距清0
- dlopen “no suitable image found ”问题之解决
- String\StringBuffer\StringBuilder
- BurpSuite导出log配合SQLMAP批量扫描注入点
- js 模块编程 module.exports require