2015-07-20 14:25:46
【传送门】
题意:给出最多50000个数,50000个询问,每个询问是统计区间【l,r】区间内每个数的出现次数,然后累加 ci^bi (表示 ci 这个数出现了 bi 次),强制在线。
思路:分块可以做,但是关键在于怎么处理涉及答案合并的询问。
一开始没什么思路,但是看了官方题解才知道只要预先统计出(1)cnt[i][j],表示数 i 在前 j 块内出现过多少次。(2)g[i][j],表示从块 i 头部到块 j 尾部这个区间的答案。
处理 cnt 和 g 数组的复杂度都是 n*sqrt(n)。对于询问,考虑最复杂的情况,区间的两边均有不完整的块a和b,中间有若干个整块a+1~b-1,首先获得中间整块的答案 g[a+1][b-1],然后暴力扫描不完整块a和b,统计这些不完整块中的数在 a+1~b-1 完整块中的出现次数(通过 cnt 数组来计算),然后再暴力扫描一边这些数,更新答案即可。
总结:精髓在于 g 数组和 cnt 数组的配合使用。
(打的比较长,效率还可以,1216MS)
1 #include <cstdio> 2 #include <ctime> 3 #include <cstring> 4 #include <cstdlib> 5 #include <cmath> 6 #include <vector> 7 #include <map> 8 #include <set> 9 #include <stack> 10 #include <queue> 11 #include <string> 12 #include <iostream> 13 #include <algorithm> 14 using namespace std; 15 16 #define getmid(l,r) ((l) + ((r) - (l)) / 2) 17 #define MP(a,b) make_pair(a,b) 18 #define PB push_back 19 20 typedef long long ll; 21 typedef pair<int,int> pii; 22 const double eps = 1e-8; 23 const int INF = (1 << 30) - 1; 24 const int MAXN = 50010; 25 const int MAX_SQR = 240; 26 const ll mod = 1e9 + 7; 27 28 int T,n,q,block,bcnt; 29 int A[MAXN],B[MAXN],C[MAXN],Bid[MAXN]; 30 int L[MAX_SQR],R[MAX_SQR]; 31 int Cnt[MAXN][MAX_SQR]; 32 int num[MAXN],sz; 33 ll G[MAX_SQR][MAX_SQR]; 34 35 vector<ll> pw[MAXN]; 36 37 void Pre(){ 38 //pre-cal power 39 for(int i = 1; i <= sz; ++i) pw[i].clear(); //清空幂向量 40 for(int i = 1; i <= n; ++i) if(pw[A[i]].size() == 0){ //处理过的不用重复处理 41 pw[A[i]].push_back(0); 42 pw[A[i]].push_back(C[i]); 43 for(int j = 2; j <= num[A[i]]; ++j){ 44 pw[A[i]].push_back(pw[A[i]][j - 1] * C[i] % mod); 45 } 46 } 47 //Cal Cnt array 48 memset(Cnt,0,sizeof(Cnt)); 49 for(int i = 1; i <= n; ++i){ 50 Cnt[A[i]][Bid[i]]++; 51 } 52 for(int i = 1; i <= sz; ++i){ 53 for(int j = 2; j <= bcnt; ++j){ 54 Cnt[i][j] += Cnt[i][j - 1]; //数i在前j块内的出现次数 55 } 56 } 57 //Cal g array 58 for(int i = 1; i <= bcnt; ++i){ 59 memset(num,0,sizeof(num)); 60 ll sum = 0; 61 for(int j = i; j <= bcnt; ++j){ 62 for(int k = L[j]; k <= R[j]; ++k){ 63 sum -= pw[A[k]][num[A[k]]++]; 64 sum += pw[A[k]][num[A[k]]]; 65 } 66 G[i][j] = sum % mod; 67 } 68 } 69 } 70 71 int Query(int l,int r){ 72 //solve left 73 int lid = Bid[l],rid = Bid[r]; 74 int m_lid = l > L[lid] ? lid + 1 : lid; 75 int m_rid = r < R[rid] ? rid - 1 : rid; 76 if(m_lid > m_rid){ //不存在中间块,直接暴力 77 memset(num,0,sizeof(num)); 78 ll res = 0; 79 for(int i = l; i <= r; ++i){ 80 res -= pw[A[i]][num[A[i]]++]; 81 res += pw[A[i]][num[A[i]]]; 82 } 83 return (res % mod + mod) % mod; 84 } 85 ll res = G[m_lid][m_rid]; 86 //pre-cal num array 87 if(l > L[lid]){ 88 for(int i = l; i <= R[lid]; ++i){ 89 num[A[i]] = Cnt[A[i]][m_rid] - Cnt[A[i]][m_lid - 1]; 90 } 91 } 92 if(r < R[rid]){ 93 for(int i = L[rid]; i <= r; ++i){ 94 num[A[i]] = Cnt[A[i]][m_rid] - Cnt[A[i]][m_lid - 1]; 95 } 96 } 97 //bf-cal non block A[i] 98 if(l > L[lid]){ 99 for(int i = l; i <= R[lid]; ++i){ 100 res -= pw[A[i]][num[A[i]]++]; 101 res += pw[A[i]][num[A[i]]]; 102 } 103 } 104 if(r < R[rid]){ 105 for(int i = L[rid]; i <= r; ++i){ 106 res -= pw[A[i]][num[A[i]]++]; 107 res += pw[A[i]][num[A[i]]]; 108 } 109 } 110 return (res % mod + mod) % mod; 111 } 112 113 int main(){ 114 int a,b,l,r; 115 scanf("%d",&T); 116 block = 400; 117 while(T--){ 118 scanf("%d%d",&n,&q); 119 for(int i = 1; i <= n; ++i){ 120 scanf("%d",A + i); 121 B[i] = C[i] = A[i]; //A是C的马甲,C是真身数据 122 Bid[i] = (i - 1) / block + 1; 123 } 124 bcnt = (n - 1) / block + 1; 125 for(int i = 1; i <= bcnt; ++i){ 126 L[i] = (i - 1) * block + 1; 127 R[i] = min(n,i * block); 128 } 129 sort(B + 1,B + n + 1); 130 sz = unique(B + 1,B + n + 1) - B - 1; //unique 返回尾元素后面的地址 131 memset(num,0,sizeof(num)); 132 for(int i = 1; i <= n; ++i){ 133 A[i] = lower_bound(B + 1,B + sz + 1,A[i]) - B; 134 num[A[i]]++; //记录马甲数的数量 135 } 136 Pre(); 137 int pre_ans = 0; 138 for(int o = 1; o <= q; ++o){ 139 scanf("%d%d",&a,&b); 140 l = (a ^ pre_ans) % n + 1; 141 r = (b ^ pre_ans) % n + 1; 142 if(l > r){ 143 int tmp = l; 144 l = r; 145 r = tmp; 146 } 147 pre_ans = Query(l,r); 148 printf("%d\n",pre_ans); 149 } 150 } 151 return 0; 152 }