根号分治

[ABC335F] Hop Sugoroku

题面翻译

有一排有 \(N\) 个方格,还有一个长度为 \(N\) 的序列 \(A\)。开始时,第一个方格被染成黑色,其他的方格为白色,并在第一个方格放上一个物品。

你可以进行若干次操作,操作如下:

  • 当物品在方格 \(i\) 的时候,可以任意选择一个正整数 \(x\),并将物品移动到方格 \(i+A_i \times x\) 上,方格 \(i+A_i \times x\) 染成黑色。

求操作结束时,可以涂成黑色的可能方格集的数量,答案对 \(998244353\) 取模。

  • \(f[i]\)表示以i结尾的所有可能的方案数
  • \(a[i]<\sqrt{N}\)时,利用cnt数组直接记录,所有满足j=i+a[i]*x的j,都可以被转移,也就是j%a[i]=i%a[i],直接在cnt[a[i]][i%a[i]]存下当前的f
  • 否则最多只会暴力转移\(\sqrt{N}\)
#include<bits/stdc++.h>
#define fo(i,a,b) for(ll (i)=(a);(i)<=(b);(i)++)
#define fd(i,a,b) for(ll (i)=(a);(i)>=(b);(i)--)
#define mk(x,y) make_pair((x),(y))
#define pi pair<ll,ll>
#define eb emplace_back 
#define fi first 
#define se second
using namespace std;
typedef long long ll;
typedef __int128 i128;
typedef unsigned int uint;
const ll mo = 998244353;
const ll inf = 1ll << 60;
const int N = 2e5 + 5;
const int S = 450;
ll cnt[S + 1][S + 1], f[N], n, a[N];
void add(ll& x, ll y) {
    x = (x + y) % mo;
}
int main() {
    // freopen("data.in", "r", stdin);
    // freopen("data.out", "w", stdout);

    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    cin >> n;
    fo(i, 1, n) cin >> a[i];

    f[1] = 1;
    fo(i, 1, n) {
        fo(j, 1, S) add(f[i], cnt[j][i % j]);

        if (a[i] > S) {
            fo(j, 1, n) {
                if (i + a[i] * j > n) break;
                add(f[i + a[i] * j], f[i]);
            }
        }
        else {
            add(cnt[a[i]][i % a[i]], f[i]);
        }
    }

    ll ans = 0;
    fo(i, 1, n) add(ans, f[i]);
    cout << ans;


    return 0;
}

[ABC259Ex] Yet Another Path Counting

题面翻译

\(N\)\(N\) 列的网格图,只能向下或向右走,合法路径的开端和结尾的格子上数字一样

找到合法路径条数,对 \(998244353\) 取模

  • 首先显然不同颜色之间互不影响
  • 设置一个阈值b,当这个颜色出现的次数小于b时,我们采用直接两两暴力的方法,时间复杂度为\(O(B^2)\),当次数大于b时,我们直接采用dp的方法,时间复杂度为\(O(n)\)\(nB^2=\frac{N^2}{B}N^2\),B=N时取等。
#include<bits/stdc++.h>
#define fo(i,a,b) for(ll (i)=(a);(i)<=(b);(i)++)
#define fd(i,a,b) for(ll (i)=(a);(i)>=(b);(i)--)
#define mk(x,y) make_pair((x),(y))
#define pi pair<ll,ll>
#define eb emplace_back 
#define fi first 
#define se second
using namespace std;
typedef long long ll;
typedef __int128 i128;
const ll mo = 998244353;
const ll inf = 1ll << 60;
const int N = 405;
vector<pi> e[N * N];
int n, a[N][N];
ll f[N][N], c[N * 2][N * 2];
void add(ll & x, ll y) {
    x = (x + y) % mo;
}
int main() {
    // freopen("data.in", "r", stdin);
    // freopen("data.out", "w", stdout);

    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    cin >> n;
    fo(i, 1, n) {
        fo(j, 1, n) {
            cin >> a[i][j];
            e[a[i][j]].eb(mk(i, j));
        }
    }

    fo(i, 0, 2 * n) c[i][0] = 1;
    fo(i, 1, 2 * n) {
        fo(j, 1, i) {
            c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mo;
        }
    }

    ll ans = 0;
    fo(i, 1, n * n) {
        if (!e[i].size()) continue;

        sort(e[i].begin(), e[i].end());

        if (e[i].size() > n) {
            fd(x, n, 1) {
                fd(y, n, 1) {
                    f[x][y] = (f[x + 1][y] + f[x][y + 1] + (a[x][y] == i)) % mo;
                    if (a[x][y] == i) add(ans, f[x][y]);
                }
            }
        }
        else {
            for (int x = 0;x < e[i].size();x++) {
                for (int y = x;y < e[i].size();y++) {
                    if (e[i][x].fi <= e[i][y].fi && e[i][x].se <= e[i][y].se) {
                        add(ans, c[e[i][y].fi - e[i][x].fi + e[i][y].se - e[i][x].se][e[i][y].se - e[i][x].se]);
                    }
                }
            }
        }
    }

    cout << ans;


    return 0;
}

[CF1207F]emainder Problem

题面翻译

题目描述

给你一个长度为 \(500000\) 的序列,初值为 \(0\) ,你要完成 \(q\) 次操作,操作有如下两种:

  1. 1 x y : 将下标为 \(x\) 的位置的值加上 \(y\)
  2. 2 x y : 询问所有下标模 \(x\) 的结果为 \(y\) 的位置的值之和
  • 对于2操作,假如\(x \le \sqrt{n}\),我们直接用一个桶维护这些x的答案,反之直接暴力即可。
#include<bits/stdc++.h>
#define fo(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)
#define fd(i,b,a) for (ll (i)=(b);(i)>=(a);(i)--)
#define eb emplace_back
#define pi pair<ll,ll>
#define mk(x,y) make_pair((x),(y))
#define lc (o<<1)
#define rc ((o<<1)|1)
#define A puts("Yes")
#define B puts("No")
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef double db;
const ll mo1=1e9+7;
const ll mo2=1e9+9;
const ll P=131;
const ll Q=13331;
const ll mo=998244353;
const ll inf=1ll<<60;
const int N=5e5+5; 
const int M=710;
ll a[N],c[M][M];
int main() {
//    freopen("data.in", "r", stdin);
//	freopen("data.out", "w", stdout);
	
	int q;
	int op,x,y;
	scanf("%d",&q);
	while (q--) {
		scanf("%d %d %d",&op,&x,&y);
		if (op==1) {
			a[x]+=y;
			fo(i,1,M-1) c[i][x%i]+=y;
		}
		else {
			if (x<M) printf("%lld\n",c[x][y]);
			else {
				ll ans=0;
				for (int i=y;i<N;i+=x) ans+=a[i];
				printf("%lld\n",ans);
			}
		}
	}
	
    return 0;
    

} 
posted @ 2024-10-24 20:30  gan_coder  阅读(2)  评论(0编辑  收藏  举报