AT1219 歴史の研究 回滚莫队
可在vj上提交:https://vjudge.net/problem/AtCoder-joisc2014_c
题意:
IOI 国历史研究的第一人——JOI 教授,最近获得了一份被认为是古代 IOI 国的住民写下的日记。JOI 教授为了通过这份日记来研究古代 IOI 国的生活,开始着手调查日记中记载的事件。
日记中记录了连续 NN 天发生的时间,大约每天发生一件。
事件有种类之分。第 ii 天发生的事件的种类用一个整数 X_iXi 表示,X_iXi 越大,事件的规模就越大。
JOI 教授决定用如下的方法分析这些日记:
-
选择日记中连续的几天 [L,R][L,R] 作为分析的时间段;
-
定义事件 AA 的重要度 W_AWA 为 A\times T_AA×TA,其中 T_ATA 为该事件在区间 [L,R][L,R] 中出现的次数。
现在,您需要帮助教授求出所有事件中重要度最大的事件是哪个,并输出其重要度。
注意:教授有多组询问。
输入格式
第一行两个空格分隔的整数 NN 和 QQ,表示日记一共记录了 NN 天,询问有 QQ 次。
接下来一行 NN 个空格分隔的整数表示每天的事件种类。
接下来 QQ 行,每行给出 L,RL,R 表示一组询问。
输出格式
输出共有 QQ 行,每行一个整数,表示对应的询问的答案。
数据范围
对于 100\%100% 的数据,1\le Q,N\le 10^51≤Q,N≤105,1\le X\le 10^91≤X≤109,1\le L\le R\le 10^51≤L≤R≤105。
输入输出样例
5 5 9 8 7 8 9 1 2 3 4 4 4 1 4 2 4
9 8 8 16 16
题解:
我们首先说一下普通莫队怎么做:
首先就是对区间排序,因为我们需要找一个区间内的最大值,这个我们可以用以变量tmp来记录。
如果要添加一个数,那么我们用cnt数组来记录某个数出现的次数,并更新tmp的值
但是如果要删除一个数,虽然我们的cnt数组还可以通过减去1来保证cnt数组正确性,但是我们没有办法去更新tmp
那么要是保证tmp正确性,我们就必须对这个区间重新遍历一次,而不能在上一个区间的前提下操作。
那么如果这样操作的话,复杂度就是n*n*sqrt(n),相信这个复杂度大多题目都会TLE了
回滚莫队解决它:
首先排序函数要改一下,如果两个查询区间的左边界不在一个块,那就按照左边界从小到大排序,否则那就按照右边界从小到大排序
ll cmp(query a, query b) { return (belong[a.l] ^ belong[b.l]) ? belong[a.l] < belong[b.l] : a.r < b.r; }
对于一个查询区间[l,r],我们把l叫做左边界,把r叫做右边界。使用左标记和右标记来在n个数的位置上移动
这样,对于左边界属于一个块(我们把n个数分成sqrt(n)个块)的查询区间,因为这些查询区间的右边界是递增的,所以右标记就只会向右移动,不会向左,就意味着不会出现删除元素的情况
但是这些查询区间的左边界可不一定和右边界的位置对应,也就是说对于右边界添加元素和删除元素的情况都会出现,怎么解决呢?
我们就暴力去解决,每次把左标记移动到一个查询区间的左边界,操作完之后在移动原位置(这一点可以看代码)
for(; belong[que[i].l]==k; ++i) //在同一个块内 { ll start=que[i].l,last=que[i].r; ll tmp; while(r<last) //因为我们的排序函数,所以我们只需要r<last和l>start两种情况 { ++r; ++cnt[typ[r]]; now=max(now,1ll*cnt[typ[r]]*arr[r]); } tmp=now; while(l > start) //将左标记回归到原位置 { --l; ++cnt[typ[l]]; now = max(now, 1ll * cnt[typ[l]] * arr[l]); } ans[que[i].id] = now; while(l < rb[k] + 1) { --cnt[typ[l]]; l++; } now = tmp; }
这个复杂度就是n*sqrt(n)。因为每次左标记都是一个固定位置,我们移动到一个查询区间的左边界这个复杂度就是sqrt(n)
因为一共有m个查询,所以m个边界(题目上说了m<=n) 所以最大复杂度就是n*sqrt(n)
然后右边界可能不在这个块内,但是右标记不需要来回移动,它只需要一直向右移动(添加元素),所以复杂度n
因为最多也就sqrt(n)个块,所以复杂度最大就是n*sqrt(n)
所以复杂度也就是n*sqrt(n)
代码:
#include <map> #include <set> #include <list> #include <queue> #include <deque> #include <cmath> #include <stack> #include <vector> #include <bitset> #include <cstdio> #include <string> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 2e5+10; const int INF = 0x3f3f3f3f; const double PI = 3.1415926; const long long N = 1000006; const double eps = 1e-10; typedef long long ll; #define mem(A, B) memset(A, B, sizeof(A)) #define lson rt<<1 , L, mid #define rson rt<<1|1 , mid + 1, R #define ls rt<<1 #define rs rt<<1|1 #define SIS std::ios::sync_with_stdiget_mod_new(z-x)o(false), cin.tie(0), cout.tie(0) #define pll pair<long long, long long> #define lowbit(abcd) (abcd & (-abcd)) #define max(a, b) ((a > b) ? (a) : (b)) #define min(a, b) ((a < b) ? (a) : (b)) inline ll read() //读取整数 { ll res = 0; char c = getchar(); while(!isdigit(c)) c = getchar(); while(isdigit(c)) res = (res << 1) + (res << 3) + (c ^ 48), c = getchar(); return res; } ll arr[maxn],cnt[maxn],cnt2[maxn],typ[maxn],inp[maxn],belong[maxn],lb[maxn],rb[maxn]; ll n,m,sizes,new_size; ll ans[maxn]; struct query { ll l,r,id; } que[maxn]; ll cmp(query a, query b) { return (belong[a.l] ^ belong[b.l]) ? belong[a.l] < belong[b.l] : a.r < b.r; } int main() { n=read(),m=read(); sizes=sqrt(n); new_size=ceil((double)n/sizes); for(ll i = 1; i <= new_size; ++i) { lb[i] = sizes * (i - 1) + 1; rb[i] = sizes * i; for(ll j = lb[i]; j <= rb[i]; ++j) belong[j] = i; } rb[new_size]=n; for(ll i=1; i<=n; ++i) arr[i]=inp[i]=read(); sort(inp+1,inp+1+n); ll tot=unique(inp+1,inp+1+n)-inp-1; for(ll i=1; i<=n; ++i) { typ[i]=lower_bound(inp+1,1+inp+tot,arr[i])-inp; } for(ll i=1; i<=m; ++i) { que[i].l=read(); que[i].r=read(); que[i].id=i; } sort(que+1,que+1+m,cmp); ll i=1; for(ll k=1; k<=new_size; ++k) { ll l=rb[k]+1,r=rb[k]; ll now=0; mem(cnt,0); for(; belong[que[i].l]==k; ++i) //在同一个块内 { ll start=que[i].l,last=que[i].r; ll tmp; if(belong[start]==belong[last]) //特判 { tmp=0; for(ll j=start;j<=last;++j) { cnt2[typ[j]]=0; } for(ll j=start; j<=last; ++j) { cnt2[typ[j]]++; tmp=max(tmp,1ll*cnt2[typ[j]]*arr[j]); } ans[que[i].id]=tmp; continue; } while(r<last) //因为我们的排序函数,所以我们只需要r<last和l>start两种情况 { ++r; ++cnt[typ[r]]; now=max(now,1ll*cnt[typ[r]]*arr[r]); } tmp=now; while(l > start) { --l; ++cnt[typ[l]]; now = max(now, 1ll * cnt[typ[l]] * arr[l]); } ans[que[i].id] = now; while(l < rb[k] + 1) { --cnt[typ[l]]; l++; } now = tmp; } } for(ll i=1; i<=m; ++i) printf("%lld\n",ans[i]); return 0; }