2025“钉耙编程”中国大学生算法设计春季联赛(10)4 7 9

关于我道心破碎,于是从新开始更新博客这件事(

1009 小塔的序列

猛地一看像类似 的一个 求最长子区间 使得里面每个出现的数字出现次数大于等于k 。
然后发现并不太像,然后 这个题很容易的想到这个题,肯定要把每个数质因数分解,然后求一个区间内值相乘,质因数个数是个偶数。

偶数,想到异或哈希,异或哈希把出现偶数次数的东西变成0.
我们把每个质因数赋个随机数,相乘表示成异或,那么我们要寻找一个最长的异或区间为0的,这个用前缀异或再加个map处理,
分解质因数和给质因数赋值可以在欧拉筛里面处理。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
#define pii pair<int, int>
#define all(a) a.begin(), a.end()
#define debug(x) cout << #x << " = " << x << endl
#define sz(a) ((int)a.size())
mt19937_64 rnd(time(nullptr));
const int mod = 998244353, N = 1e6 + 10, G = 3;
ll b[N];
ll c[N];
unordered_map<ll, ll> mp;
void solve()
{
    int n;
    scanf("%d", &n);
    mp.clear();
    int ans = 0;
    int l, r;
    int x;
    ll y = 0;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &x);
        y ^= b[x];
        if (!mp[y])
        {
            mp[y] = i;
        }
        if (y == 0)
        {
            ans = i;
            l = 1, r = i;
        }
        if (mp[y])
        {
            if (i - mp[y] > ans)
            {
                ans = i - mp[y];
                l = mp[y] + 1;
                r = i;
            }
        }
    }
    if (ans > 0)
        PDF("%d %d\n", l, r);
    else
        printf("-1 -1\n");
}
ll pri[N];
ll pan[N];
int main()
{
    int t = 1;
    scanf("%d", &t);
    int cnt = 0;
    for (int i = 2; i <= 1e6; i++)
    {
        if (!pan[i])
        {
            b[i] = rnd();
            pri[++cnt] = i;
        }
        for (int j = 1; j <= cnt && (long long)pri[j] * i <= 1e6; j++)
        {
            pan[i * pri[j]]++;
            b[i * pri[j]] = b[i] ^ b[pri[j]];
            if (i % pri[j] == 0)
                break;
        }
    }
    while (t--)
    {
        solve();
    }
    return 0;
}

1004 小塔的随机数

\(n\)\(m\)都很小,尝试了几个数发现,如果按他给的区间整体来算发现无从下手,但是因为这个题很小的\(n,m\)可以跑个\(n*m*n\)的复杂度,我们可以思考用贡献求解的可能性。
\(f[i][j]\)\(i\),\(j\)是一个逆序的概率
发现 每一次随机化l r ,如果

  • \({r>=i>=l \ and \ r<=j<=i}\) 时,\(f[i][j]=1/2\)
  • \(r>=i>=l\)\(f[i][j]=(\sum_{k=l}^{r} f[k][j])/(r-l+1)\)
  • \(r>=j>=l\)\(f[i][j]=(\sum_{k=l}^{r} f[i][k])/(r-l+1)\)
    最后统计\(f[i][j]\)即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
#define pii pair<int, int>
#define all(a) a.begin(), a.end()
#define debug(x) cout << #x << " = " << x << endl
#define sz(a) ((int)a.size())
mt19937_64 rnd(time(nullptr));
const int mod = 1e9 + 7, N = 1e5 + 10, G = 3;
ll fastmi(ll base, ll power)
{
    ll ans = 1;
    base = base % mod;
    while (power)
    {
        if (power & 1)
        {
            ans = ans * base % mod;
        }
        base = base * base % mod;
        power >>= 1;
    }
    return ans;
}
ll dao[N];
void solve()
{
    ll n, m;
    cin >> n >> m;
    vector<vector<ll>> f(n + 1, vector<ll>(n + 1, 0));
    for (int _ = 1; _ <= m; _++)
    {
        int l, r;
        cin >> l >> r;
        vector<ll> fl(n + 1, 0);
        for (int i = 1; i < l; i++)
        {
            for (int j = l; j <= r; j++)
                fl[i] = (fl[i] + f[i][j]) % mod;
            fl[i] = fl[i] * dao[r - l + 1] % mod;
        } // 左边不在 右边在
        vector<ll> fr(n + 1, 0);
        for (int j = r + 1; j <= n; j++)
        {
            for (int i = l; i <= r; i++)
                fr[j] = (fr[j] + f[i][j]) % mod;
            fr[j] = fr[j] * dao[r - l + 1] % mod;
        }
        for (int i = 1; i <= n; i++)
            for (int j = i + 1; j <= n; j++)
            {
                if (i >= l && i <= r && j >= l && j <= r)
                    f[i][j] = dao[2];
                else if (i >= l && i <= r)
                    f[i][j] = fr[j];
                else if (j >= l && j <= r)
                    f[i][j] = fl[i];
            }
    }
    ll ans = 0;
    for (int i = 1; i <= n; i++)
        for (int j = i + 1; j <= n; j++)
            ans = (ans + f[i][j]) % mod;
    cout << ans << '\n';
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    for (int i = 1; i <= 500; i++)
    {
        dao[i] = fastmi(i, mod - 2);
    }
    while (t--)
    {
        solve();
    }
    return 0;
}

1007 小塔的魔法树

发现数据很小,然后我们可以跑一个n*m的时间复杂度
每个点的子树有m种取值,有1种转移可以考虑是一个DFS序上DP

把树搞成dfs序,
dp[i][j]表示位于dfs序i,能得到的魔力值时j的方案数

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
#define pii pair<int, int>
#define all(a) a.begin(), a.end()
#define debug(x) cout << #x << " = " << x << endl
#define sz(a) ((int)a.size())
// mt19937_64 rnd(time(nullptr));
const int mod = 1e9 + 7, N = 5e3 + 10, G = 3;
vector<int> edge[N];
void solve()
{
    ll n, m;
    cin >> n >> m;
    vector<ll> a(n + 1);
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    for (int i = 1; i < n; ++i)
    {
        int u, v;
        cin >> u >> v;
        edge[u].pb(v);
        edge[v].pb(u);
    }
    vector<int> dfn(n + 2);
    vector<int> dfn_ma(n + 2);
    int cnt = 0;
    auto dfs = [&](auto self, int u, int father) -> void
    {
        int now = ++cnt;
        dfn[now] = u;
        for (auto it : edge[u])
        {
            if (it == father)
                continue;
            self(self, it, u);
        }
        dfn_ma[now] = cnt + 1;
        edge[u].clear();
    };
    dfs(dfs, 1, 0);

    vector<vector<ll>> f(n + 2, vector<ll>(m + 1));
    f[1][0] = 1;
    for (int i = 1; i <= n; ++i)
    {
        for (int j = 0; j <= m - a[dfn[i]]; ++j)
        {
            f[i + 1][j + a[dfn[i]]] = f[i + 1][j + a[dfn[i]]] + f[i][j] % mod;
        }
        for (int j = 0; j <= m; ++j)
        {
            f[dfn_ma[i]][j] = (f[dfn_ma[i]][j] + f[i][j]) % mod;
        }
    }

    ll ans = 0;
    for (int i = 0; i <= m; i++)
        ans = ans + f[n + 1][i] % mod;
    cout << (ans + mod - 1) % mod << '\n';
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}
posted @ 2025-05-17 17:30  _3449  阅读(112)  评论(0)    收藏  举报