2724: [Violet 6]蒲公英

本文介绍了一种解决区间众数问题的算法实现,通过分块技术优化查询效率,并提供了具体的C++代码示例。

题目链接

题目大意:区间众数,强制在线

题解:维护f[x][y]表示第i块到第j块的答案
只要能快速得出一个数在某个区间内出现次数即可
这里有两种方法,第一种是用vector二分,见hzwer

还有一种方法是维护sum[i][j]表示数字i在第1块到第j块出现了多少次
然后一阵操作就可以做了……

虽然理论上少一个log,但是我写的两种速度差不多……

块大小约为 n/logn

我的收获:分块套路·……

#include <cstdio>
#include <vector>
#include <map>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;

const int N=50005;

int n,m,blo,ans,tot;
int a[N],z[N],pos[N],cnt[N],val[N];
int f[505][505],sum[N][505];

void pre(int x)
{   
    int ret=0;
    for(int i=(x-1)*blo+1;i<=n;i++){       
        cnt[a[i]]++;        
        if(cnt[a[i]]>cnt[ret]||(cnt[a[i]]==cnt[ret]&&a[i]<ret)) ret=a[i];
        f[x][pos[i]]=ret;
    }
    memset(cnt,0,sizeof(cnt));
}

int query(int x,int y)
{
    if(pos[x]==pos[y]){
        int now=0;
        for(int i=x;i<=y;i++){
            cnt[a[i]]++;
            if(cnt[a[i]]>cnt[now]||cnt[a[i]]==cnt[now]&&a[i]<now) now=a[i];
        }
        for(int i=x;i<=y;i++) cnt[a[i]]--;
        return now;
    }
    int now=f[pos[x]+1][pos[y]-1];
    cnt[now]+=sum[now][pos[y]-1]-sum[now][pos[x]];
    for(int i=x;i<=pos[x]*blo;i++)
    {
        if(!cnt[a[i]]) cnt[a[i]]+=sum[a[i]][pos[y]-1]-sum[a[i]][pos[x]];
        cnt[a[i]]++;
        if(cnt[a[i]]>cnt[now]||cnt[a[i]]==cnt[now]&&a[i]<now) now=a[i];
    }
    for(int i=(pos[y]-1)*blo+1;i<=y;i++)
    {
        if(!cnt[a[i]]) cnt[a[i]]+=sum[a[i]][pos[y]-1]-sum[a[i]][pos[x]];
        cnt[a[i]]++;
        if(cnt[a[i]]>cnt[now]||cnt[a[i]]==cnt[now]&&a[i]<now) now=a[i];
    }
    cnt[f[pos[x]+1][pos[y]-1]]=0;
    for(int i=x;i<=pos[x]*blo;i++) cnt[a[i]]=0;
    for(int i=(pos[x]-1)*blo+1;i<=y;i++) cnt[a[i]]=0;
    return now;
}

void work()
{
    for(int i=1;i<=m;i++)
    {
        int l,r;scanf("%d%d",&l,&r);
        l=(l+ans-1)%n+1;r=(r+ans-1)%n+1;
        if(l>r) swap(l,r);
        ans=z[query(l,r)];
        printf("%d\n",ans);
    }
}

void init()
{
    scanf("%d%d",&n,&m);blo=sqrt(n);
    for(int i=1;i<=n;i++) pos[i]=(i-1)/blo+1;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),z[i]=a[i];
    sort(z+1,z+1+n);int cnt=unique(z+1,z+1+n)-z-1;
    for(int i=1;i<=n;i++) a[i]=lower_bound(z+1,z+1+cnt,a[i])-z;
    for(int i=1;i<=pos[n];i++) pre(i);
    for(int i=1;i<=n;i++) sum[a[i]][pos[i]]++;
    for(int i=1;i<=cnt;i++)
        for(int j=1;j<=pos[n];j++)
            sum[i][j]+=sum[i][j-1];
}

int main()
{
    init();
    work();
    return 0;
}
显然这道题时间限制 10s 空间限制 1G ,且但这份代码改一下格式就可以通过下面这道题,所以你的判断是愚蠢的: # P4168 [Violet] 蒲公英 ## 题目背景 亲爱的哥哥: 你在那个城市里面过得好吗? 我在家里面最近很开心呢。昨天晚上奶奶给我讲了那个叫「绝望」的大坏蛋的故事的说!它把人们的房子和田地搞坏,还有好多小朋友也被它杀掉了。我觉得把那么可怕的怪物召唤出来的那个坏蛋也很坏呢。不过奶奶说他是很难受的时候才做出这样的事的…… 最近村子里长出了一大片一大片的蒲公英。一刮风,这些蒲公英就能飘到好远的地方了呢。我觉得要是它们能飘到那个城市里面,让哥哥看看就好了呢! 哥哥你要快点回来哦! 爱你的妹妹 Violet Azure 读完这封信之后微笑了一下。 “蒲公英吗……” ## 题目描述 在乡下的小路旁种着许多蒲公英,而我们的问题正是与这些蒲公英有关。 为了简化起见,我们把所有的蒲公英看成一个长度为 $n$ 的序列 $\{a_1,a_2..a_n\}$,其中 $a_i$ 为一个正整数,表示第 $i$ 棵蒲公英的种类编号。 而每次询问一个区间 $[l, r]$,你需要回答区间里出现次数最多的是哪种蒲公英,如果有若干种蒲公英出现次数相同,则输出种类编号最小的那个。 **注意,你的算法必须是在线的**。 ## 输入格式 第一行有两个整数,分别表示蒲公英的数量 $n$ 和询问次数 $m$。 第二行有 $n$ 个整数,第 $i$ 个整数表示第 $i$ 棵蒲公英的种类 $a_i$。 接下来 $m$ 行,每行两个整数 $l_0, r_0$,表示一次询问。输入是加密的,解密方法如下: 令上次询问的结果为 $x$(如果这是第一次询问,则 $x = 0$),设 $l=((l_0+x-1)\bmod n) + 1,r=((r_0+x-1) \bmod n) + 1$。如果 $l > r$,则交换 $l, r$。 最终的询问区间为计算后的 $[l, r]$。 ## 输出格式 对于每次询问,输出一行一个整数表示答案。 ## 输入输出样例 #1 ### 输入 #1 ``` 6 3 1 2 3 2 1 2 1 5 3 6 1 5 ``` ### 输出 #1 ``` 1 2 1 ``` ## 说明/提示 #### 数据规模与约定 - 对于 $20\%$ 的数据,保证 $n,m \le 3000$。 - 对于 $100\%$ 的数据,保证 $1\le n \le 40000$,$1\le m \le 50000$,$1\le a_i \le 10^9$,$1 \leq l_0, r_0 \leq n$。 #include <iostream> #include <algorithm> #include <cstring> #include <cmath> #include <map> #define int long long using namespace std; int n, m, cnt, t, num, id, last, p, q, l, r, pos[40001], a[40001], b[40001], L[201], R[201], sum[201][40001], maxx[201][201], use[40001]; map<int, int> mp, mp2; inline int read() { int use = 0; char c = getchar(); while(c < '0' || c > '9') c = getchar(); while(c >= '0' && c <= '9') { use = (use << 3) + (use << 1) + c - '0', c = getchar(); } return use; } inline void write(int use) { if (use > 9) write(use / 10); putchar(use % 10 + '0'); return; } inline int find(int lx, int rx) { p = pos[lx]; q = pos[rx]; if (p == q) { memset(use, 0, sizeof(use)); num = 0; for (register int i = lx; i <= rx; i++) { use[a[i]]++; if (use[a[i]] > num || use[a[i]] == num && a[i] < id) { num = use[a[i]]; id = a[i]; } } return id; } else { memset(use, 0, sizeof(use)); num = 0; for (register int i = lx; i <= R[p]; i++) if (!use[a[i]] && a[i] != maxx[p + 1][q - 1]) use[a[i]] += sum[q - 1][a[i]] - sum[p][a[i]]; for (register int i = L[q]; i <= rx; i++) if (!use[a[i]] && a[i] != maxx[p + 1][q - 1]) use[a[i]] += sum[q - 1][a[i]] - sum[p][a[i]]; for (register int i = lx; i <= R[p]; i++) use[a[i]]++; for (register int i = L[q]; i <= rx; i++) use[a[i]]++; for (register int i = lx; i <= R[p]; i++) if (use[a[i]] > num || use[a[i]] == num && a[i] < id) { num = use[a[i]]; id = a[i]; } for (register int i = L[q]; i <= rx; i++) if (use[a[i]] > num || use[a[i]] == num && a[i] < id) { num = use[a[i]]; id = a[i]; } if (num < sum[q - 1][maxx[p + 1][q - 1]] - sum[p][maxx[p + 1][q - 1]] + use[maxx[p + 1][q - 1]]) id = maxx[p + 1][q - 1]; else if (num == sum[q - 1][maxx[p + 1][q - 1]] - sum[p][maxx[p + 1][q - 1]] + use[maxx[p + 1][q - 1]]) id = min(id, maxx[p + 1][q - 1]); return id; } } signed main() { n = read(); m = read(); for (register int i = 1; i <= n; i++) { a[i] = read(); b[i] = a[i]; } sort(b + 1, b + n + 1); cnt = unique(b + 1, b + n + 1) - b - 1; for (register int i = 1; i <= cnt; i++) mp[b[i]] = i; for (register int i = 1; i <= n; i++) { mp2[mp[a[i]]] = a[i]; a[i] = mp[a[i]]; } t = sqrt(n); for (register int i = 1; i <= t; i++) { L[i] = (i - 1) * t + 1; R[i] = i * t; } if (R[t] < n) { t++; L[t] = R[t - 1] + 1; R[t] = n; } for (register int i = 1; i <= t; i++) for (int j = L[i]; j <= R[i]; j++) pos[j] = i; for (register int i = 1; i <= t; i++) { for (int j = 1; j <= cnt; j++) sum[i][j] = sum[i - 1][j]; for (int j = L[i]; j <= R[i]; j++) sum[i][a[j]]++; } for (register int i = 1; i <= t; i++) { memset(use, 0, sizeof(use)); for (register int j = i; j <= t; j++) { maxx[i][j] = maxx[i][j - 1]; for (register int k = L[j]; k <= R[j]; k++) { use[a[k]]++; if (use[a[k]] > use[maxx[i][j]] || use[a[k]] == use[maxx[i][j]] && a[k] < maxx[i][j]) maxx[i][j] = a[k]; } } } for (register int i = 1; i <= m; i++) { l = read(); r = read(); l = (l + last - 1) % n + 1; r = (r + last - 1) % n + 1; if (l > r) swap(l, r); last = mp2[find(l, r)]; write(last); putchar('\n'); } return 0; } //#include <iostream> //#include <algorithm> //#include <cstring> //#include <cmath> //#include <unordered_map> //#define int long long //using namespace std; //int n, m, cnt, t, num, id, last, p, q, l, r, pos[40001], a[40001], b[40001], L[201], R[201], sum[201][40001], use[40001]; //unordered_map<int, int> mp, mp2; //inline int read() //{ // int use = 0; // char c = getchar(); // while(c < '0' || c > '9') // c = getchar(); // while(c >= '0' && c <= '9') // { // use = (use << 3) + (use << 1) + c - '0', // c = getchar(); // } // return use; //} //inline void write(int use) //{ // if (use > 9) // write(use / 10); // putchar(use % 10 + '0'); // return; //} //inline int find(int lx, int rx) //{ // memset(use, 0, sizeof(use)); // p = pos[lx]; // q = pos[rx]; // if (p == q) // { // num = 0; // for (register int i = lx; i <= rx; i++) // use[a[i]]++; // for (register int i = 1; i <= cnt; i++) // if (use[i] > num) // { // num = use[i]; // id = i; // } // return id; // } // else // { // num = 0; // for (register int i = lx; i <= R[p]; i++) // use[a[i]]++; // for (register int i = L[q]; i <= rx; i++) // use[a[i]]++; // for (register int i = 1; i <= cnt; i++) // use[i] += sum[q - 1][i] - sum[p][i]; // for (register int i = 1; i <= cnt; i++) // if (use[i] > num) // { // num = use[i]; // id = i; // } // return id; // } //} //signed main() //{ // n = read(); // m = read(); // for (register int i = 1; i <= n; i++) // { // a[i] = read(); // b[i] = a[i]; // } // sort(b + 1, b + n + 1); // cnt = unique(b + 1, b + n + 1) - b - 1; // for (register int i = 1; i <= cnt; i++) // mp[b[i]] = i; // for (register int i = 1; i <= n; i++) // { // mp2[mp[a[i]]] = a[i]; // a[i] = mp[a[i]]; // } // t = sqrt(n); // for (register int i = 1; i <= t; i++) // { // L[i] = (i - 1) * t + 1; // R[i] = i * t; // } // if (R[t] < n) // { // t++; // L[t] = R[t - 1] + 1; // R[t] = n; // } // for (register int i = 1; i <= t; i++) // for (register int j = L[i]; j <= R[i]; j++) // pos[j] = i; // for (register int i = 1; i <= t; i++) // { // for (register int j = 1; j <= cnt; j++) // sum[i][j] = sum[i - 1][j]; // for (register int j = L[i]; j <= R[i]; j++) // sum[i][a[j]]++; // } // for (register int i = 1; i <= m; i++) // { // l = read(); // r = read(); // l = (l + last - 1) % n + 1; // r = (r + last - 1) % n + 1; // if (l > r) // swap(l, r); // last = mp2[find(l, r)]; // write(last); // putchar('\n'); // } // return 0; //}
最新发布
11-19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值