在这片梦想之地,不堪回首的过去像泡沫一样散|

PassName

园龄:3年1个月粉丝:32关注:16

ARC167

ARC167

前言

非常不可做的一场,全场数学,打得心累。

[ARC167A] Toasts for Breakfast Party

传送门link

对于任意一个盘子价值形如 (xi+xj)2 的形式,那么所有的价值加起来一定是 xi2+xjxk 现在要最小化后面那个

排个序即可。

signed main() 
{
    cin >> n >> m;
    for (rint i = 1; i <= n; i++) cin >> a[i];
    sort (a + 1, a + 2 * m + 1);
    int ans = 0;
    for (rint i = 1; i <=  2 * m; i++) ans += a[i] * a[i];
    for (rint i = 1; i <= m; i++) ans += 2 * a[i] * a[2 * m - i + 1];
    cout << ans << endl;
    return 0;
}

[ARC167B] Product of Divisors

传送门link

A=p1m1p2m2pkmk,有 AB=(p1m1p2m2pkmk)B=p1m1×Bp2m2×Bpkmk×B

考虑每个质因数 pi 对乘积贡献多少次

枚举其他质因数的方案为 ji(mjB+1),而枚举这个质因数的次数的范围为 0miB,答案为

(0+1++miB)ji(mjB+1)=miB(mjB+1)2

由于 A 中原本就有 mi

最后的答案为 Ans=B2(miB+1)

int a, b, B;
int m;
bool flag;
int ans;

void solve(int x) 
{
    int i = 2;
    while (i <= sqrt(x)) 
	{
        int cnt = 0;
        while (!(x % i))
        {
            x /= i; 
			cnt++;			
		}
        m = (cnt * b + 1) % mod * m % mod; 
        if ((cnt & 1) && (B & 1)) flag = 0;
        bool v = i != 2;
		i += 1 + v;
    }

    if (x > 1)
    {
        m = m * (b + 1) % mod;
		flag = 0;		
	}
}

signed main()
{
    cin >> a >> b;
    B = b;
    flag = b & 1;
    m = b %= mod;
    solve(a);
    ans = (m - flag) * 499122177 % mod;
    cout << (ans % mod + mod) % mod << endl;
    return 0;
}

[ARC167C] MST on Line++

传送门link

做不出来,官方题解也搞不懂,最后看的洛谷第一篇题解

考虑计算当前的 ai 会成为多少个边权。

aip 重新排列后,i 需要在前面选择一个父亲,则选择父亲的这条边的权值为 ai 当且仅当前面的 ka 中存在一个 aj 使得 ajaj,其余的随便排

i 向儿子的全部连边中,边权为 ai 当且仅当儿子 j 前面的 ka 均比 aj 大且 ai 为最小的一个。则选出 k 个比 ai 大的数进行排列并钦定这个排列右边的第一个数比 ai 小即可

int n, k;
int a[N];
int fac[N], ifac[N], inv[N];
int ans;

void init()
{
    fac[0] = 1;
    fac[1] = inv[1] = 1;
    for (rint i = 2; i < N; i ++)
    {
        fac[i] = fac[i - 1] * i % mod;
        inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    }	
    ifac[0] = ifac[1] = 1;
    for (rint i = 2; i < N; i++)
    {
		ifac[i] = inv[i] * ifac[i - 1] % mod;
	}
}

int C(int a, int b)
{
	if (b < 0 || a < b) return 0;
	return fac[a] * ifac[a - b] % mod * ifac[b] % mod;
}

signed main() 
{
    cin >> n >> k;

    init();

    for (rint i = 1; i <= n; i++) cin >> a[i];

    sort(a + 1, a + n + 1);

    for (rint i = 1; i <= n; i++) 
	{
        int add, t;
        
		for (rint j = 1; j < k; j++)
        {
            add = a[i] * (C(n - 1, j) - C(n - i, j) + mod);
            t = n - j - 1;
			ans += add % mod * fac[j] % mod * fac[t] % mod;			
		}
		
        add = a[i] * (n - k) % mod * (C(n - 1, k) - C(n - i, k) + mod);
        t = n - k - 1;
        
		ans += add % mod * fac[k] % mod * fac[t] % mod;

        for (rint j = 1; j < k; j++)
        {
            add = a[i] * C(n - i, j - 1) % mod;
            t = n - j - 1;
			ans += add * fac[j] % mod * fac[t] % mod * (i - 1) % mod;			
		}
		
		t = n - k - 1;
		add = a[i] * (n - k) % mod * C(n - i, k - 1) % mod;
        ans += add * fac[k] % mod * fac[t] % mod * (i - 1) % mod;
        
        ans %= mod;
    }
    
    cout << ans << endl;
    
    return 0;
}

[ARC167D] Good Permutation

传送门link

连边 iPi,形成了若干个不相交的环。

P 是一个好的排列当且仅当图上只有一个环。

从小到大依次贪心确定每一位的值,如果存在至少一个 Pj=u,j>ii 为当前位置),i,j 不在一个环且 u<Pi,找到最小的 uswap(Pi,Pj)

否则不希望字典序变大,尽量不换。但如果 i 是它所在连通块的最后一个位置,必须要换,找到后面最小的 u,设 Pj=u,并 swap(Pi,Pj)

判断是否在一个环使用并查集即可

#define X it->x
#define Y it->y

using namespace std;

const int N = 2e5 + 5;

int n;
int a[N];
int fa[N], minn[N];
int sz[N], p[N];

struct node
{
	int x, y;
	friend bool operator < (node a, node b) 
	{
	    return a.x < b.x;
	}
};

set<node> s;

int find(int x) 
{
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}

void merge(int a, int b) 
{
    int x = find(a);
	int y = find(b);
    if (x != y) 
    {
		s.erase({minn[x], x});
		s.erase({minn[y], y});
	    fa[x] = y;
		sz[y] += sz[x]; 
		minn[y] = min(minn[y], minn[x]);
		s.insert({minn[y], y});
	}
}

signed main() 
{
    int T;
    cin >> T;

    while (T--) 
	{
        s.clear();
        cin >> n;;

        for (rint i = 1; i <= n; i++)
        {
			cin >> a[i];
			p[a[i]] = fa[i] = minn[i] = i;
			sz[i] = 1;
			s.insert({i, i});
		}

        for (rint i = 1; i <= n; i++) merge(i, a[i]);

        for (rint i = 1; i <= n; i++) 
		{
            if (s.size() == 1) break;
            auto it = s.begin();
            while (find(i) == find(Y)) it++;
            if (X < a[i] || sz[find(i)] == 1) 
			{
                int j = p[X];
                swap(a[i], a[j]);
				swap(p[a[i]], p[a[j]]);
                merge(i, j);
            }
            sz[find(i)]--;
        }

        for (rint i = 1; i <= n; i++) cout << a[i] << " ";

        cout << endl;
    }

    return 0;
}

[ARC167E] One Square in a Triangle

传送门link

三角形平移对答案没有影响,设一个点为 (0,0)

面积 S2,定一个点为 (2,0),然后把三角形高度设为 S2 。因为不能再产生其它正方形,所以让一条斜边与正方形有一个交点,因为后面肯定这条斜边与另一条斜边的距离会变小,不可能再出现一个正方形。第三个点取 (S2,S2)

考虑 S 是奇数的情况。一个点在 (0,0),设另外两个点为 (x1,y1)(x2,y2),则有 S=|x1y2x2y1|,假设 x1y2 为奇数,接着给 x1x2 一个值。给个 31S=|3y2y1|,考虑去绝对值,让 y1<3y2,因为这两个奇偶性不同,让 3y2y1=1 就可以了,解出来 y1=S32,y2=S12

有一些无解的情况要特判,偶数的情况发现只有 2 无解,奇数 1,3,5,7 无解。

signed main()
{
	cin >> T;
	while (T--)
	{
		int s;
		cin>> s;
		if (s == 1 | s == 2 || s == 3 || s == 5 || s == 7) puts("No");
		else
		{
			puts("YES");
			if (s & 1) cout << "0 0 1 3 " << (s - 1) / 2 << " " << (s - 3) / 2 << endl;
			else cout << "0 0 2 0 " << s / 2 << " " << s / 2 << endl;
		}
	}
	return 0;
}

[ARC167F] Tree Tree Tree

传送门link

Question

给你整数 NK,使得 2KN.

问题:potato

有一棵加权有根树,其顶点为 N ,编号为 1N。顶点 1 是根。

对于每个 2iN,点 i 的父顶点是 pi(1pi<i),连接 ipi 的边的权重是 qi1

这里,q=(q1,q2,,qN1)(1,2,,N1) 的排列。

假设 cost(u,v) 是连接顶点 uv 的简单路径中一条边的最大权重。

找到 u=1Nv=u+1Ncost(u,v)

问题:tomato

给你一个整数 a,使得 1a<K。在上面的问题中,pq 可能有 ((N1)!)2K1 对,且 pK=a。求所有这些对的答案之和,模数998244353

求每个 a=1,,K1 的问题 tomato 的答案。

Solution

如果我们固定 p,并让 Ai 表示路径长度为 i 的顶点 u,v(u<v) 对的数目,那么所有 q 的 "potato " 问题答案之和可以用序列 B=(B1,,BN1) 表示为 i=0N1AiBi。因此,"tomato " 问题的答案可以表示为 [xN1](fa(x)g(x)),其中 [xi]fa(x) 是顶点 uv 之间的路径长度为 ipK=a 以及 g(x)=j=1N1BjxN1j 的三元组 (p,u,v) 的个数。

首先,很容易看出 Bj=N!jj+1.

让我们根据路径的类型分解 fa(x) 并分别计算每一部分。

fa1(x) : 连接顶点 uv 且至少不包含其中一个顶点 aK 的路径的数目。

fa2(x) : 连接顶点 uv 且包含顶点 aK,且 uv 的 LCA 为 a 的路径数。

fa3(x) : 连接顶点 uv 且包含顶点 aK,且 uv 的 LCA 不为 a 的路径条数。

对于 fa1(x)

hi(x) 是从 fa1(x) 中提取 LCA 为 1iN 的项得到的多项式,则下式成立:

hi(x)=12(2ji,jKj1)i<jN,jK((j1)+2x).

这里,hi(x) 的常数项可以是任何项(因为它不会影响结果)。因此可以在O(Nlog2(N)) 中通过分治计算出来

对于 fa2(x)

fa2(x)=x(a1)!j=a+1K1((j1)+x)j=K+1N((j1)+2x)

对于 fa3(x)

fa3(x)=x2i=1a1((i1)!j=i+1a1((j1)+2x)j=a+1K1((j1)+x))j=K+1N((j1)+2x)

虽然无法在规定的时间内明确计算出 fa2(x)fa3(x) ,但适当地去除不必要的项,计算出 O(Nlog2(N)) 中所有 a[xN1](fa2(x)g(x))[xN1](fa3(x)g(x))

仅以 fa3 为例子

对于任意整数 0lr<K ,定义 x 的多项式 Xl,r,Yl,r,Zl,r,Wl,r,DP1l,r,DP2l,r 如下:

  • Xl,r=i=lr1max(i,1)

  • Yl,r=i=lr1(i+2x)

  • Zl,r=i=lr1(i+x)

  • Wl,r=j=l+1rXl,jYj,r

  • DP1l,r=g(x)x2X0,lZr,K1j=KN1(j+2x)

  • DP2l,r=g(x)x2W0,lZr,K1j=KN1(j+2x)

由于 g(x)fa3(x)=DP2a1,a 成立,所以只要找到所有 1a<K[xN1]DP2i1,i 即可。

对于任意整数 0l<m<rK ,下面的条件成立

DP1l,m=DP1l,rZm,r

DP1m,r=DP1l,rXl,m DP2l,m=DP2l,rZm,r

DP2l,r=DP2l,rYl,m+DP1l,rWl,m

由于 DP10,K1DP20,K1 很容易找到,所以从这里开始,选择 mm=l+r2,就可以在 O(logK) 多项式乘法中找到 DP2a1,a

在计算 DP1DP2 之前,应先计算用于求 DP2a1,aX,Y,Z,W

由于 Yl,r,Zl,r,Wl,r 最多是 (rl) 阶多项式,随着 rl 的减小,被乘多项式的阶数也随之减小

因此,在预计算中进行多项式乘法和加法运算的多项式的总度数为 O(KlogK),所以预计算可以在时间复杂度为 O(Klog2K) 的情况下完成。

除了求 DP10,K1 所需的 O(Nlog2N) 时间外,DP1l,rDP2l,r 的计算也可以在 O(Klog2K) 时间内完成,因为求 [xN1]DP2a1,a 时最多只需保留 O(rl) 项。

Code

#include <bits/stdc++.h>

#define rint register int
#define int long long
#define endl '\n'

using namespace std;

const int N = 2e5 + 5;
const int M = 3e5 + 5;
const int mod = 998244353;

int n, K;
int ans[N], rev[M], w[M];
int maxn;
int fac[N], inv[N];
int tot;

template<typename T>
void upd(int &a, T b) 
{
    a = (a + b) % mod;
}

int qpow(int a, int b)
{
	int res = 1;
	while (b)
	{
		if (b & 1) res = res * a % mod;
		b >>= 1;
		a = a * a % mod;
	}
	return res;
}

void init()
{
    fac[0] = fac[1] = inv[0] = inv[1] = 1;
    for (rint i = 2; i <= n; i++) fac[i] = fac[i - 1] * i % mod;
    for (rint i = 2; i <= n; i++) inv[i] = (mod - mod / i) * inv[mod % i] % mod;	
}

void init(int len) 
{
    int l = 0;
    maxn = 1;

    while (maxn <= len)
    {
        maxn <<= 1;
		l++;		
	}  

    for (rint i = 0; i < maxn; i++)
    {
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (l - 1));		
	}

    for (rint i = 1; i < maxn; i <<= 1) 
	{
        int kk = qpow(3, (mod - 1) / (i << 1));
        w[i] = 1;
        for (rint j = 1; j < i; j++)
        {
            w[i + j] = w[i + j - 1] * kk % mod;			
		}
    }
}

void NTT(vector<int> &p, bool flag) 
{
    static unsigned long long a[M];
    p.resize(maxn);

    for (rint i = 0; i < maxn; i++)
    {
        a[i] = p[rev[i]];		
	}

    for (rint i = 1; i < maxn; i <<= 1)
    {
        for (rint j = 0; j < maxn; j += i << 1)
        {
            for (rint k = j; k < j + i; k++) 
			{
                unsigned long long x = a[k], y = a[k + i] * w[i + k - j] % mod;
                a[k] = x + y;
                a[k + i] = x + mod - y;
            }			
		}
	}

    if (flag)
    {
        for (rint i = 0; i < maxn; i++)
        {
            p[i] = a[i] % mod;				
		}	
	}

    else 
	{
        reverse(a + 1, a + maxn);
        int inv = qpow(maxn, mod - 2);
        for (rint i = 0; i < maxn; i++) p[i] = a[i] % mod * inv % mod;
    }
}

vector<int> operator+(const vector<int> &a, const vector<int> &b) 
{
    int n = a.size(), m = b.size();
    vector<int> ans(max(n, m));
    for (rint i = 0; i < n; i++) ans[i] = a[i];
    for (rint i = 0; i < m; i++) upd(ans[i], b[i]);
    return ans;
}

vector<int> operator-(const vector<int> &a, const vector<int> &b) 
{
    int n = a.size(), m = b.size();
    vector<int> ans(max(n, m));
    for (rint i = 0; i < n; i++) ans[i] = a[i];
    for (rint i = 0; i < m; i++) upd(ans[i], mod - b[i]);
    return ans;
}

vector<int> operator*(vector<int> a, int k) 
{
    for (rint &i : a) i = i * k % mod; return a;
}

vector<int> mul(vector<int> a, vector<int> b, int Type = -1) 
{
    int n = a.size(), m = b.size();
    if (Type < 0) Type = n + m - 1;
    init(n + m);
    NTT(a, 1), NTT(b, 1);
    for (rint i = 0; i < maxn; i++) a[i] = a[i] * b[i] % mod;
    NTT(a, 0), a.resize(Type);
    return a;
}

vector<int> mul__(vector<int> a, vector<int> b, int Type = -1) 
{
    int n = a.size(), m = b.size();
    if (Type < 0) Type = n - m + 1;
    a.resize(m + Type - 1);
    reverse(b.begin(), b.end());
    init(m + Type - 1);
    NTT(a, 1), NTT(b, 1);
    for (rint i = 0; i < maxn; i++) a[i] = a[i] * b[i] % mod;
    NTT(a, 0);
    move(a.begin() + m - 1, a.begin() + m + Type - 1, a.begin());
    a.resize(Type);
    return a;
}

struct node 
{
    vector<int> P0, P1, P2, Ps;
    node operator+(const node &x)const 
	{
        node ans;
        ans.P0 = mul(P0, x.P0);
        ans.P1 = mul(P1, x.P1);
        ans.P2 = mul(P2, x.P2);
        ans.Ps = mul(Ps, x.P1) + mul(P0, x.Ps);
        return ans;
    }
} val[M];

struct node2 
{
    vector<int> P0, P1, Ps;
    node2 operator+(const node2 &x)const 
	{
        node2 ans;
        ans.P0 = mul(P0, x.P0);
        ans.P1 = mul(P1, x.P1);
        ans.Ps = mul(P0, x.Ps) + mul(Ps, x.P1);
        return ans;
    }
};

void build(int p, int l, int r) 
{
    if (l == r) 
	{
        val[p].P0 = {max(l - 1, 1ll), 0};
        val[p].P1 = {max(l - 1, 1ll), 1};
        val[p].P2 = {max(l - 1, 1ll), (mod + 1) / 2};
        val[p].Ps = {0, max(l - 1, 1ll)};
        return;
    }

    int mid = (l + r) / 2;
    build(p << 1, l, mid);
    build(p << 1 | 1, mid + 1, r);
    val[p] = val[p << 1] + val[p << 1 | 1];
}

vector<int> solve1(int l, int r) 
{
    if (l == r) return {max(l - 1, 1ll), 1};
    int mid = (l + r) >> 1;
    return mul(solve1(l, mid), solve1(mid + 1, r));
}

node2 solve2(int l, int r) 
{
    if (l == r) 
	{
        node2 ans;

        if (l != K) 
		{
            ans.P0 = {max(l - 1, 1ll), 0};
            ans.P1 = {max(l - 1, 1ll), 1};
            ans.Ps = {0, max(l - 1, 1ll)};
        } 
		else
		{
            ans.P0 = ans.P1 = {1, 0}; 
			ans.Ps = {0, 1};			
		}
        return ans;
    }

    int mid = (l + r) >> 1;
    return solve2(l, mid) + solve2(mid + 1, r);
}

void solve3(int p, int l, int r, const vector<int> &u, const vector<int> &d) 
{
    if (l == r) return upd(ans[l], (mod + 1) / 2 * u[1] + max(l - 1, 1ll) * d[1]);
    int mid = (l + r) >> 1;
    solve3(p * 2, l, mid, mul__(u, val[p * 2 + 1].P2), mul__(d, val[p * 2 + 1].P2));
    solve3(p * 2 + 1, mid + 1, r, mul__(u, val[p * 2].P1) + mul__(d, val[p * 2].Ps), mul__(d, val[p * 2].P0));
}

void calc()
{
    vector<int> F = K < n ? mul({0, 1}, solve1(K + 1, n)): (vector<int>) {0, 1}, G(n + 1);
    for (rint i = 2, pw = 1; i <= n; pw = pw * 2 % mod, i++) G[i] = pw * inv[i] % mod; G = mul__(G, F);
    vector<int> H = solve2(1, n).Ps;
    int t = 0;
    for (rint i = 2, pw = 1; i <= n; pw = pw * 2 % mod, i++) upd(t, pw * inv[i] % mod * H[i]);
    fill(ans + 1, ans + K, t); solve3(1, 1, K - 1, vector<int>(G.size()), G);
    tot = n * (n - 1) / 2 % mod * fac[n - 1] % mod * inv[K - 1] % mod;	
}

signed main() 
{
    cin >> n >> K;

    init(); build(1, 1, K - 1);
    
    calc();

    for (rint i = 1; i < K; i++)
    {
		int rs = (tot - ans[i] + mod) % mod * fac[n] % mod;
		cout << rs << endl;
	}

    return 0;
}

本文作者:PassName

本文链接:https://www.cnblogs.com/spaceswalker/p/17976432

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   PassName  阅读(37)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起