P4396 [AHOI2013]作业 分块+莫队
这个题正解是莫队+树状数组,但是我个人非常不喜欢树状数组这种东西,所以决定用分块来水这个题。直接在莫队维护信息的时候,维护单点同时维护块内信息就行了。
莫队就是这几行核心代码:
void add(int x) { ++f[bl[x]];//维护块 ++cnt[x];//维护点 if(cnt[x] == 1) g[bl[x]]++; } void del(int x) { --f[bl[x]]; --cnt[x]; if(!cnt[x]) g[bl[x]]--; } void moqueue() { int l = 1,r = 0; duke(i,1,m) { while(l > G[i].l) add(s[--l]); while(l < G[i].l) del(s[l++]); while(r < G[i].r) add(s[++r]); while(r > G[i].r) del(s[r--]); work(G[i].a,G[i].b,i); } }
剩下就是暴力了,说真的,这个作法真的暴力,但是就是能过。哈哈哈。
题干:
题目描述 此时己是凌晨两点,刚刚做了Codeforces的小A掏出了英语试卷。英语作业其实不算多,一个小时刚好可以做完。然后是一个小时可以做完的数学作业,接下来是分别都是一个小时可以做完的化学,物理,语文......小A压力巨大。 这是小A碰见了一道非常恶心的数学题,给定了一个长度为n的数列和若干个询问,每个询问是关于数列的区间表示数列的第1个数到第r个数),首先你要统计该区间内大于等于a,小于等于b的数的个数,其次是所有大于等于a,小于等于b的,且在该区间中出现过的数值的个数。 小A望着那数万的数据规模几乎绝望,只能向大神您求救,请您帮帮他吧。 输入输出格式 输入格式: 第一行n,m 接下来n个数表示数列 接下来m行,每行四个数l,r,a,b 输出格式: 输出m行,分别对应每个询问,输出两个数,分别为在1到i?这段区间中大小在[a,b]中的数的个数,以及大于等于a,小于等于b的,且在该区间中出现过的数值的个数(具体可以参考样例)。 输入输出样例 输入样例#1: 复制 3 4 1 2 2 1 2 1 3 1 2 1 1 1 3 1 3 2 3 2 3 输出样例#1: 复制 2 2 1 1 3 2 2 1 说明 N<=100000,M<=100000
代码:
#include<iostream> #include<cstdio> #include<cmath> #include<ctime> #include<queue> #include<algorithm> #include<cstring> using namespace std; #define duke(i,a,n) for(int i = a;i <= n;i++) #define lv(i,a,n) for(int i = a;i >= n;i--) #define clean(a) memset(a,0,sizeof(a)) const int INF = 1 << 30; typedef long long ll; typedef double db; template <class T> void read(T &x) { char c; bool op = 0; while(c = getchar(), c < '0' || c > '9') if(c == '-') op = 1; x = c - '0'; while(c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; if(op) x = -x; } template <class T> void write(T x) { if(x < 0) putchar('-'), x = -x; if(x >= 10) write(x / 10); putchar('0' + x % 10); } const int N=200010; struct edge{ int l,r,a,b; int ans1,ans2; int id; }G[N]; int cnt[N]; int bl[N]; int blk; int n,m; int s[N]; int f[N],g[N]; bool cmp(edge x,edge y) { if(bl[x.l] != bl[y.l]) return bl[x.l] < bl[y.l]; else return x.r < y.r; } void add(int x) { ++f[bl[x]]; ++cnt[x]; if(cnt[x] == 1) g[bl[x]]++; } void del(int x) { --f[bl[x]]; --cnt[x]; if(!cnt[x]) g[bl[x]]--; } void work(int l,int r,int x) { if(bl[l] == bl[r]) { duke(i,l,r) { if(cnt[i]) G[x].ans1 += cnt[i],G[x].ans2 ++; } } else { lv(i,bl[l] * blk - 1,l) if(cnt[i]) G[x].ans1 += cnt[i],G[x].ans2 ++; duke(i,bl[r] * blk - blk,r) if(cnt[i]) G[x].ans1 += cnt[i],G[x].ans2 ++; duke(i,bl[l] + 1,bl[r] - 1) G[x].ans1 += f[i],G[x].ans2 += g[i]; } } void moqueue() { int l = 1,r = 0; duke(i,1,m) { while(l > G[i].l) add(s[--l]); while(l < G[i].l) del(s[l++]); while(r < G[i].r) add(s[++r]); while(r > G[i].r) del(s[r--]); work(G[i].a,G[i].b,i); } } bool cmp2(edge a,edge b) { return a.id < b.id; } int main() { read(n);read(m); duke(i,1,n) read(s[i]); blk = sqrt(n); duke(i,1,m) bl[i] = (i) / blk + 1; duke(i,1,m) { read(G[i].l);read(G[i].r); read(G[i].a);read(G[i].b); G[i].id = i; } sort(G + 1,G + m + 1,cmp); moqueue(); sort(G + 1,G + m + 1,cmp2); duke(i,1,m) { printf("%d %d\n",G[i].ans1,G[i].ans2); } return 0; } /* 3 4 1 2 2 1 2 1 3 1 2 1 1 1 3 1 3 2 3 2 3 */
代码:
只想找一个不会伤害我的人