CF86D. Powerful array
http://www.codeforces.com/contest/86/problem/D
跟很久之前的集训题目小Z的袜子一样的题目,没法通过什么数据结构有效的解决。不过有个通用的解法,莫队算法,可以把询问的区间[l,r]看成平面图上的点(l,r),则我们需要找一条哈密顿路径(只需要遍历所有的点,不是严格哈密顿),从点(x1,y1)->(x2,y2)需要修改的复杂度可以认为是abs(x1-x2)+abs(y1-y2),于是总复杂度相当于这条路径的曼哈顿距离和。求最小的距离和我不会,但是可以把问题转化成求的曼哈顿最小生成树,有证明该树权值和不超过n*sqrt(m),遍历该树所有点的路径的距离为2*n*sqrt(m),已经可以接受。最后求最小生成树的算法时间复杂度是n*logn,有兴趣的读者可以上网搜索一下。。
不过上面的算法实在不好写,问了一下我队友学了另外一个更加简单高效暴力的方法,将每个询问[l,r],根据l大小将询问分别扔进sqrt(n)个桶,之后每个桶再根据右区间从小到大排序,则解决每个桶时候,右区间由于单调递增总共最多需要移动n次,共n*sqrt(n)。。同时每个桶内左区间暴力移动,由于左区间只在一个桶内移动,所以单次操作只需要sqrt(n),总共也是n*sqrt(n)...具体实现如下:
View Code
//By Lin #include<cstdio> #include<cstring> #include<vector> #include<algorithm> #define maxn 200050 #define sqr(x) ((x)*(x)) using namespace std; typedef long long LL; int n,m,len,kcnt; struct Node{ int num,l,r; Node(int x ,int y ,int z):num(x),l(y),r(z){} friend bool operator<( const Node &a , const Node &b ){ return a.r < b.r; } }; vector<Node> g[500]; int num[1000050],data[maxn]; LL now,ans[maxn]; int main(){ scanf("%d%d", &n, &m ); for (int i = 0; i<n; i++) scanf("%d", &data[i] ); for (len = 0; sqr(len+1)<=n; len++); kcnt = n%len==0?n/len:(n/len+1); for (int i = 0; i<m; i++) { int l , r; scanf("%d%d", &l, &r ); l--,r--; g[l/len].push_back( Node(i,l,r) ); } for (int i = 0; i<kcnt; i++ ){ sort( g[i].begin() , g[i].end() ); int l = i*len , r = i*len-1; now = 0; for (int k = 0; k<g[i].size(); k++ ){ while ( r<g[i][k].r ){ r++; now -= data[r]*sqr((LL)num[data[r]]); num[data[r]]++; now += data[r]*sqr((LL)num[data[r]]); } while ( l<g[i][k].l ){ now -= data[l]*sqr((LL)num[data[l]]); num[data[l]]--; now += data[l]*sqr((LL)num[data[l]]); l++; } while ( l>g[i][k].l ){ l--; now -= data[l]*sqr((LL)num[data[l]]); num[data[l]]++; now += data[l]*sqr((LL)num[data[l]]); } ans[g[i][k].num] = now; // printf("%d %d %lld\n" , l , r , now ); } for (int i = l; i<=r; i++ ) num[data[i]]--; now = 0; } for (int i = 0; i<m; i++) printf("%I64d\n", ans[i] ); return 0; }