ABC335(E-G)

E

如果加上每个点AiA_iAi不同的条件限制,那么题目就是简单的dp
AiA_iAi值从小到大fu=max⁡(fv)+1f_u=\max (f_v)+1fu=max(fv)+1对于(v,u)(v,u)(v,u)点对
或者用拓扑排序从点1开始
但是点的A值可能是相同的,如何判定谁在前面?
可以采用计算连通子图时常见的策略,将同A值并连接在一起的点缩成一个点。我这里用的是并查集。


拓扑排序可以建图来看,从权值小的点向权值大的点连一条有向边,同样会遇到相同权值的情况。当两个权值Au,AvA_u,A_vAu,Av相等时,(u,v)(u,v)(u,v)(v,u)(v,u)(v,u)都有一条边,这时候会出现环
需要用上面缩点的方法去掉环

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <algorithm>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;


pii a[200020];
int n, m;
int f[200020];
vi g[200020];
int fa[200020];
int sz[200020];
const int inf = 1 << 29;


int getf(int u) {
    if (fa[u] == u)
        return u;
    return fa[u] = getf(fa[u]);
}

void merge(int x, int y) {
    int fx = getf(x), fy = getf(y);
    if (sz[fy] < sz[fx]) swap(fx, fy);
    fa[fx] = fy;
    sz[fy] += sz[fx];
}


int main(){
    //freopen("in.txt", "r", stdin);
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) {
        int t;
        cin >> t;
        a[i] = { t, i };
        fa[i] = i;
        sz[i] = 1;
    }
    for (int i = 0; i < m; ++i) {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
        if (a[u].first == a[v].first) {
            merge(u, v);
        }
    }
    for (int i = 1; i <= n; ++i) {
        f[i] = -inf;
    }
    sort(a + 1, a + n + 1);
    int f1 = getf(1);
    for (int i = 1; i <= n; ++i) {
        int u = a[i].second;
        int fu = getf(u);
        if (fu == f1) {
            f[fu] = 1;
        }
        else {
            for (int v : g[u]) {
                int fv = getf(v);
                if(fv != fu)
                    f[fu] = max(f[fu], f[fv] + 1);
            }
        }
    }
    int fn = getf(n);
    if (f[fn] < 0) 
        f[fn] = 0;
    printf("%d\n", f[fn]);
    return 0;
}

F

首先观察例子,很容易得到一个推论:
f(i)=∑f(i+kai)+1f(i)=\sum f(i+ka_i)+1f(i)=f(i+kai)+1
暴力不可取,容易退化到O(n2)O(n^2)O(n2)
由于有i+kaii+ka_ii+kai这种形式,会联想到分组
aia_iai大的时候∑f(i+kai)\sum f(i+ka_i)f(i+kai)和中的数据项较少,可以直接做
aia_iai小的时候怎么办?
由于项中的下标有统一规律pos≡i( mod ai)pos \equiv i (\bmod a_i)posi(modai),故考虑维护一数组g[s][r]g[s][r]g[s][r]记录从右往左遍历得到的aia_iai被s除得到余数r的总和
f(i)=g[a[i]][i%a[i]]f(i)=g[a[i]][i \% a[i]]f(i)=g[a[i]][i%a[i]]

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <algorithm>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;

int n;
int a[200010];
ll mod = 998244353;
ll f[200010];
ll r[500][500];

#define addm(x, y) (x = (x + y) % mod)


int main(){
    //freopen("in.txt", "r", stdin);
    cin >> n;
    int m = sqrt(n);
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }
    for (int i = n; i >= 1; --i) {
        if (a[i] > m) {
            for (int j = 1; j * a[i] + i <= n; ++j) {
                addm(f[i], f[j * a[i] + i]);
            }
        }
        else {
            f[i] = r[a[i]][i % a[i]];
        }
        addm(f[i], 1);
        for (int j = 1; j <= m; ++j) {
            addm(r[j][i % j], f[i]);
        }
    }
    printf("%lld\n", f[1]);
    return 0;
}

G

不难,但是很棒
首先需要知道什么是阶以及怎么求阶
假设Aik≡AjA_i^k \equiv A_jAikAj
δi\delta_iδiAiA_iAi的阶,δj\delta_jδjAjA_jAj的阶
Aiδik≡1≡Aikδi≡AjδiA_i^ {\delta_ik} \equiv 1 \equiv A_i^{k\delta_i} \equiv A_j^{\delta_i}Aiδik1AikδiAjδi
Ajδj≡1A_j^{\delta_j} \equiv 1Ajδj1
∴Ajδj≡Ajδi\therefore A_j^{\delta_j} \equiv A_j^{\delta _i}AjδjAjδi
∴δj∣δi\therefore \delta_j | \delta_iδjδi
最后一步证明略,可参见初等数论阶的章节。
本题需要128位整数,因此用python来写,pypy效率和g++比起来也不算太差,而且代码简洁了好多

# -*- coding: utf-8 -*-
# @time     : 2023/6/2 13:30
# @file     : atcoder.py
# @software : PyCharm

import sys
from collections import defaultdict
sys.setrecursionlimit(100010)


def parse(x):
    i = 2
    fac = []
    while i * i <= x:
        if x % i == 0:
            fac.append(i)
            while x % i == 0:
                x //= i
        i += 1
    if x != 1:
        fac.append(x)
    return fac


def get_order(a, X, fac):
    d = X - 1
    for p in fac:
        while d % p == 0 and pow(a, d // p, X) == 1:
            d //= p
    return d


def main():
    items = sys.version.split()
    fp = open("in.txt") if items[0] == "3.10.6" else sys.stdin
    n, p = map(int, fp.readline().strip().split())
    a = list(map(int, fp.readline().strip().split()))
    fac = parse(p - 1)
    h = defaultdict(int)
    for x in a:
        r = get_order(x, p, fac)
        h[r] += 1
    ords = h.keys()
    ans = sum(h[i] * h[j] for i in ords for j in ords if i % j == 0)
    print(ans)


if __name__ == "__main__":
    main()


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值