hdu5722 Jewelry
题意就是说问有多少个区间,其中有至少一种种类的宝珠出现的次数恰好为x次。
先预处理出每一个位置的宝珠下一个出现与其同种类的宝珠位置next和上一个出现与其同种类的位置pre
考虑在第i个位置的宝珠,要使其出现恰好x次,我们可以找到在i之后恰好出现了x次的位置j,这步操作可以用一定的技巧优化到O(1),那么对于区间[l,r],l∈[pre[i],i],r∈[j,next[j]],都满足题目要求的答案。
如何统计呢,可以把每次可行的区间看作一个矩形,底为[pre[i],i],高位[j,next[j]],那么最后的答案就是这些矩形并的面积了
那么就是经典的线段树问题了,时间复杂度O(nlogn)
1 #include<cstdio> 2 #include<algorithm> 3 #include<map> 4 #include<cstring> 5 using namespace std; 6 const int N = 1001010; 7 struct g{ 8 int l,r,typ,h; 9 }v[N]; 10 long long ans; 11 int n,i,j,a[N],x,tot; 12 int Next[N],pre[N],flag[N],s[N]; 13 map<int,int> pos,tmp; 14 bool cmp(g a,g b) 15 { 16 return a.h<b.h; 17 } 18 void change(int x,int a,int b,int l,int r,int c) 19 { 20 if ((a<=l)&&(r<=b)) 21 { 22 flag[x]+=c; 23 if (flag[x]) s[x]=r-l;else s[x]=s[2*x]+s[2*x+1]; 24 return; 25 } 26 int m=(l+r)>>1; 27 if (a<m) change(2*x,a,b,l,m,c); 28 if (m<b) change(2*x+1,a,b,m,r,c); 29 if (flag[x]) s[x]=r-l; 30 else s[x]=s[2*x]+s[2*x+1]; 31 } 32 int main() 33 { 34 int test; 35 scanf("%d",&test); 36 while (test--) 37 { 38 scanf("%d%d",&n,&x); 39 pos.clear();tmp.clear(); 40 memset(flag,0,sizeof(flag)); 41 memset(pre,0,sizeof(pre)); 42 memset(Next,0,sizeof(Next)); 43 memset(s,0,sizeof(s)); 44 tot=0;ans=0; 45 for (i=1;i<=n;i++) 46 { 47 scanf("%d",&a[i]); 48 Next[pos[a[i]]]=i; 49 pre[i]=pos[a[i]]; 50 pos[a[i]]=i; 51 } 52 for (i=1;i<=n+1;i++) 53 if (Next[i]==0) Next[i]=n+1; 54 for (i=1;i<=n;i++) 55 { 56 if (tmp[a[i]]==0) 57 { 58 tmp[a[i]]=i; 59 for (j=1;j<x;j++) 60 tmp[a[i]]=Next[tmp[a[i]]]; 61 } 62 else 63 tmp[a[i]]=Next[tmp[a[i]]]; 64 if (tmp[a[i]]!=n+1) 65 { 66 v[++tot].l=pre[i]+1;v[tot].r=i;v[tot].h=tmp[a[i]];v[tot].typ=1; 67 v[++tot].l=pre[i]+1;v[tot].r=i;v[tot].h=Next[tmp[a[i]]];v[tot].typ=-1; 68 } 69 } 70 sort(v+1,v+1+tot,cmp); 71 for (i=1;i<=tot;i++) 72 { 73 ans+=(long long)(v[i].h-v[i-1].h)*s[1]; 74 change(1,v[i].l-1,v[i].r,0,n,v[i].typ); 75 } 76 printf("%I64d\n",ans); 77 } 78 }