学习计划——根号分治

根号分治

  • 根号分治是一种思想,一般根据一个数(可以是数组的数,也可以是答案的数)分类,分为大于 s q r t ( n ) sqrt(n) sqrt(n)的部分和小于等于 s q r t ( n ) sqrt(n) sqrt(n)的部分

  • 题目链接: luogu.com.cn/problem/P3396

  • 解题思路: 根号分治的经典题目。我们根据模数分类,模数大于等于 s q r t ( n ) sqrt(n) sqrt(n)的部分我们暴力求解,小于等于的部分我们提前预处理出来就好了

const int N = 2e5 + 5;
int n , m , a[N];
int dp[N][405];
int main ()
{
    CLOSE;
    cin >> n >> m;
    for (int i = 1 ; i <= n ; i ++)
        cin >> a[i];
    int p = sqrt (n);
    for (int i = 1 ; i <= n ; i ++)
    {
        for (int j = 1 ; j <= p ; j ++)
            dp[j][i%j] += a[i];
    }
    while (m --)
    {
        string op;
        int x, y;
        cin >> op >> x >> y;
        if (op == "A")
        {
            if (x <= p) cout << dp[x][y] << endl;
            else
            {
                ll ans = 0;
                for (int j = y ; j <= n ; j += x)
                    ans += a[j];
                cout << ans << endl;
            }
        }
        else
        {
            for (int j = 1 ; j <= p ; j ++)
                dp[j][x%j] += (y-a[x]);
            a[x] = y;
        }
    }
}
  • 题目链接: https://codeforces.com/problemset/problem/1446/D2
  • 解题思路: 本题与简单版本的差异在于出现数的种类,那么我们按每个数出现的次数根号分治,那么出现次数大于等于 s q r t ( n ) sqrt(n) sqrt(n)的数种类一定是不超过 s q r t ( n ) sqrt(n) sqrt(n)那么这一部分我们可以和 E e s y Eesy Eesy部分同样处理,接下来是出现次数小于等于 s q r t ( n ) sqrt(n) sqrt(n)的这一部分。显然我们也需要根据 s q r t ( n ) sqrt(n) sqrt(n)这个复杂度来。所以,我们枚举最终序列中最大出现次数为 x x x x x x属于 [ 1 , s q r t ( n ) ] [1,sqrt(n)] [1,sqrt(n)]。接下来可以利用尺取得做法求出每个 x x x得最大值
const int N = 2e5 + 5;
int n , buck[N], a[N];
int mx, val, cnt;
int ans = 0;
unordered_map <int,int> ma;
void solve1 (int x)
{
    ma.clear();
    int v = 0;
    ma[0] = 0;
    for (int i = 1 ; i <= n ; i ++)
    {
        if (a[i] == x) v ++;
        if (a[i] == val) v --;
        if (!ma.count(v))
            ma[v] = i;
        else
            ans = max (ans , i - ma[v]);
    }
}
void solve2 (int x)
{
    for (int i = 1 ; i <= n ; i ++) buck[i] = 0;
    int l = 1, type = 0;
    buck[a[l]] ++;
    if (buck[a[l]] == x) type ++;
    for (int i = 2 ; i <= n ; i ++)
    {
        buck[a[i]] ++;
        if (buck[a[i]] == x) type ++;
        else if (buck[a[i]] > x)
        {
            if (buck[a[i]] == x + 1) type --;
            while (buck[a[i]] > x)
            {
                buck[a[l]] --;
                if (buck[a[l]] == x) type ++;
                else if (buck[a[l]] == x - 1) type --;
                l ++;
            }
        }
        if (type >= 2) ans = max (ans, i - l + 1);
    }
}
int main ()
{
    CLOSE;
    cin >> n;
    mx = 0, val = 0, cnt = 0;
    for (int i = 1 ; i <= n ; i ++) {
        cin >> a[i], buck[a[i]]++;
        if (buck[a[i]] > mx)
            mx = buck[a[i]], val = a[i], cnt = 1;
        else if (buck[a[i]] == mx)
            cnt ++;
    }
    if (cnt > 2)
    {
        cout << n << endl;
        return 0;
    }
    int limit = sqrt (n);
    for (int i = 1 ; i <= n ; i ++)
        if (buck[i] >= limit && i != val)
            solve1 (i);
    for (int i = 1 ; i < limit ; i ++)
        solve2 (i);
    cout << ans << endl;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值