2024“钉耙编程”中国大学生算法设计超级联赛(3) 1005 数论

题意:

分析:

远看数论题,实则是道数据结构。

在固定 \(gcd\) 的情况下\(f_{i}\) 表示 \(r_{k}=i\) 的方案数,\(g_{i}\) 表示 \(l_{1}=i\) 的方案数,那么运用简单容斥,可得:

\[ans_{x} += (\sum_{i=1}^{n} f_{i}) - ((\sum_{i=1}^{x-1}f_{i})+1) \times ((\sum_{i=x+1}^{n}g_{i})+1)+1 \]

先考虑如何计算 \(f_{i}\),对于一个相同的 \(i\)

\(z=\gcd(a_{x},a_{x+1},\dots,a_{i})\),可以发现随着 \(x\) 变小,\(z\) 单调不升且 \(z\) 的本质不同的值不超过 \(\log V\) 个。因此可以使用二分把所有 \(z\) 相同的左端点的区间求出来,对于 \(z\) 相同的一段左端点 \(x \in [l,r]\) 而言,它在 \(dp\) 转移时必须满足上一个区间的 \(gcd\) 与这个区间的 \(gcd\) 值相等,解决方法也不难,只需要先二分离线预处理出一些四元组:

\[(l,r,i,z) \]

其表示左端点所属的区间为 \([l,r]\),右端点为 \(i\)\(gcd\)\(z\)。根据上面的分析,这样的四元组的个数不超过 \(n \log V\) 个。我们将 \(z\) 相同的四元组按 \(i\) 排序,记 \(sum_{i}\) 表示在该 \(gcd\)\(\sum_{j=1}^{i}f_{i}\),那么转移为:

\[(\sum_{j=l-1}^{r-1} s_{j}) \to f_{i} \]

转移它可以使用线段树,套路地维护 \(f_{i}\)\(if_{i}\) 的前缀和,这样就能快速求出 \(s_{j}\) 的前缀和,这样就能转移了。容易发现,所求得 \(g_{i}\)\(a_{i}\) 翻转后的 \(f_{n-i+1}\)

因此对于同一 \(gcd\) 而言,得到了\(f\)\(g\) 的若干点值,由于相邻两段点值对中间一段的 \(ans_{i}\) 的贡献是相同的,因此可以使用差分维护。总时间复杂度 \(O(n \log n \log V)\)

放一份卡常失败,但是过了大样例的代码

#include <bits/stdc++.h>
#define int long long
#define N 100005
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#define mod 998244353
using namespace std;

int n, Sum; 
int a[N], ans[N];

struct Tree {
    int c[N * 4], tag1[N * 4], tag2[N * 4]; 

    void init() {
        memset(c, 0, sizeof(c));
        memset(tag1, 0, sizeof(tag1)); 
        memset(tag2, 0, sizeof(tag2)); 
    }

    void pushup(int u) {
        c[u] = (c[u * 2] + c[u * 2 + 1]) % mod;
    }

    void maketag(int u, int L, int R, int tg1, int tg2) {
        if(tg1 != -1) {
            tag1[u] = tg1;
            tag2[u] = tg2;
            c[u] = (tg1 + tg2) * (R - L + 1) % mod;
        }
        else {
            tag2[u] = (tag2[u] + tg2) % mod;
            c[u] = (c[u] + tg2 * (R - L + 1) % mod) % mod;
        }
    }

    void pushdown(int u, int L, int R) {
        if(tag1[u] == -1 && tag2[u] == 0) return;
        int mid = (L + R) / 2;
        maketag(u * 2, L, mid, tag1[u], tag2[u]);
        maketag(u * 2 + 1, mid + 1, R, tag1[u], tag2[u]);
        tag1[u] = -1; tag2[u] = 0;
    }

    void update(int u, int L, int R, int l, int r, int x, int y) {
        if(R < l || r < L) return;
        if(l <= L && R <= r) {
            maketag(u, L, R, x, y);
            return;
        }
        int mid = (L + R) / 2;
        pushdown(u, L, R);
        update(u * 2, L, mid, l, r, x, y);
        update(u * 2 + 1, mid + 1, R, l, r, x, y);
        pushup(u);
    }

    int query(int u, int L, int R, int l, int r) {
        if(R < l || r < L) return 0;
        if(l <= L && R <= r) return c[u];
        int mid = (L + R) / 2;
        pushdown(u, L, R);
        return (query(u * 2, L, mid, l, r) + query(u * 2 + 1, mid + 1, R, l, r)) % mod;
    }
}t1, t2; 

struct node {
    int l, r, i, z; 
};
vector<node>t, now;

bool cmp(node A, node B) {
    if(A.z != B.z) return A.z < B.z;
    else return A.i < B.i;
}

int Gcd[N][25], lg[N];

int Get_gcd(int L, int R) {
    int h = lg[R - L + 1];
    return __gcd(Gcd[L][h], Gcd[R - (1 << h) + 1][h]);
}

int ask(int x) {
    if(x <= 0) return 0;
    return ((x + 1) * t1.query(1, 1, n, x, x) % mod - t2.query(1, 1, n, x, x) + mod) % mod; 
}

unordered_map<int, int>h; 
int cnt;

struct ljm {
    int opt, i, x; //opt=1:f opt=2:g  f[i]=x
};
vector<ljm>B[N * 35];
int tiao[N];
void work(int Gcd, int opt) {
    t1.update(1, 1, n, 1, n, 0, 0);
    t2.update(1, 1, n, 1, n, 0, 0);
    
    if(!h[Gcd]) {
    	h[Gcd] = ++cnt;
		tiao[cnt] = Gcd;
	}
    Gcd = h[Gcd];

    for(auto x : now) {
        int res = ask(x.r - 1) - ask(x.l - 2) + (x.r - x.l + 1);
		t1.update(1, 1, n, x.i, n, -1, res);
        t2.update(1, 1, n, x.i, n, -1, res * x.i % mod);
        if(opt == 1) B[Gcd].push_back((ljm){1, x.i, res});
        else B[Gcd].push_back((ljm){2, n - x.i + 1, res});
        if(opt == 1) Sum = (Sum + res) % mod;
    }
}

void Sol(int opt) {
    t1.init(); t2.init();
    t.clear();
    for(int i = 2; i <= n; i++) lg[i] = lg[i / 2] + 1;
    for(int i = 1; i <= n; i++) Gcd[i][0] = a[i];
    for(int j = 1; j <= 20; j++)
    for(int i = 1; i + (1 << j) - 1 <= n; i++) Gcd[i][j] = __gcd(Gcd[i][j - 1], Gcd[i + (1 << (j - 1))][j - 1]);

    //cout << "look : " << Get_gcd(1, 5) << endl;

    for(int i = 1; i <= n; i++) {
        int r = i, l;
        while(r) {
            int res = Get_gcd(r, i);
            int L = 1, R = r, Get = 0, mid;
            while(L <= R) {
                mid = (L + R) / 2;
                if(Get_gcd(mid, i) >= res) {
                    Get = mid;
                    R = mid - 1;
                }
                else L = mid + 1;
            }
            l = Get;
            t.push_back((node){l, r, i, res});
            //if(res == 2) 
			//printf("l = %lld, r = %lld, i = %lld, res = %lld \n", l, r, i, res);
            r = l - 1;
        }
    }

    sort(t.begin(), t.end(), cmp);
    int L = 0;
    while(L < t.size()) {
        now.clear();
        int R = L;
        now.push_back(t[L]);
        while(R + 1 < t.size() && t[R + 1].z == t[L].z) now.push_back(t[++R]);
        //if(t[L].z == 2)
		work(t[L].z, opt);
        L = R + 1;
    }
}

bool cmp2(ljm a, ljm b) {
    return a.i < b.i;
}
int v[N], F[N], G[N], len;
void Get_ans(int x) {
	
    sort(B[x].begin(), B[x].end(), cmp2);
    
    /*printf("Gcd = %lld : \n", tiao[x]);
	for(auto now : B[x]) {
		if(now.opt == 1) printf("f[%lld] = %lld \n", now.i, now.x);
		else printf("g[%lld] = %lld \n", now.i, now.x);
	}*/
	//printf("\n");
	
    len = 0;
    int lst = 0;
    for(auto now : B[x]) {
        if(now.i == lst) {
            if(now.opt == 1) F[len] += now.x;
            else G[len] += now.x; 
        }
        else {
            lst = now.i;
            v[++len] = now.i;
            if(now.opt == 1) F[len] += now.x;
            else G[len] += now.x; 
        }
    }

    for(int i = 1; i <= len; i++) F[i] = (F[i] + F[i - 1]) % mod;
    for(int i = len; i >= 1; i--) G[i] = (G[i] + G[i + 1]) % mod;

    for(int i = 0; i <= len; i++) {
    	int L = v[i] + 1, R;
    	if(i == len) R = n;
    	else R = v[i + 1] - 1;
    	if(L <= R) {
    		int res = ((F[i] + 1) * (G[i + 1] + 1) % mod - 1 + mod) % mod;
    		ans[L] = (ans[L] + res) % mod;
    		ans[R + 1] = (ans[R + 1] - res + mod) % mod;
    		//printf("opt : [%lld, %lld] += %lld \n", L, R, res);
		}
	}
	for(int i = 1; i <= len; i++) {
		int res = ((F[i - 1] + 1) * (G[i + 1] + 1) % mod - 1 + mod) % mod;
		int L = v[i], R = v[i];
		ans[L] = (ans[L] + res) % mod;
    	ans[R + 1] = (ans[R + 1] - res + mod) % mod;
    	//printf("opt : [%lld, %lld] += %lld \n", i, i, res);
	}
	//printf("\n \n");

    for(int i = 1; i <= len; i++) v[i] = F[i] = G[i] = 0;
}

signed main() {
	//freopen("x.in", "r", stdin);
	//freopen("x.out", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
   	//cerr << "e";
	Sol(1); 
	reverse(a + 1, a + n + 1);
    Sol(2);

    for(int i = 1; i <= cnt; i++) Get_ans(i);
    for(int i = 1; i <= n; i++) ans[i] = (ans[i - 1] + ans[i]) % mod;
    //cerr << "Sum : " << Sum << endl;
    //Sum = 24;
    
    for(int i = 1; i <= n; i++) {
    	cout << (Sum - ans[i] + mod) % mod << " ";
	}
    return 0;
}

/*
6
3 6 12 2 4 1
*/ 
posted @ 2024-07-30 20:01  小超手123  阅读(39)  评论(1编辑  收藏  举报