• JZOJ 【2021.11.10NOIP提高组联考】


    简要题解

    这套题比较 (H_2O)
    建议题目背景美文共赏

    ( ext{T1})

    显然一个 (O(n^3)) 不能过的 (dp)
    然而过了?!
    用心在该卡时间的地方卡一卡

    ( ext{Code})

    #include <cstdio>
    #include <algorithm>
    #define re register 
    using namespace std;
    typedef long long LL;
    
    const int N = 5005;
    int n, m, C, lim[N], cnt, col[N];
    LL f[N][N], g[N];
    struct node{int v, w, c;}a[N];
    inline bool cmp(node a, node b){return a.c < b.c;}
    inline bool cmp1(node a, node b){return a.v > b.v;}
    
    inline void solve()
    {
    	sort(a + 1, a + n + 1, cmp1);
    	LL ans = 0;
    	for(re int i = 1; i <= n; i++)
    	{
    		if (!m) break;
    		if (lim[a[i].c]) ans += a[i].v, --lim[a[i].c], --m;
    	}
    	printf("%lld
    ", ans);
    }
    
    int main()
    {
    	freopen("diversity.in", "r", stdin), freopen("diversity.out", "w", stdout);
    	scanf("%d%d%d", &n, &m, &C);
    	int bz1 = 1, bz2 = 1;
    	for(re int i = 1; i <= n; i++) scanf("%d%d%d", &a[i].v, &a[i].w, &a[i].c), bz2 = (bz2 && (a[i].w == 1));
    	sort(a + 1, a + n + 1, cmp);
    	for(re int i = 1; i <= C; i++) scanf("%d", &lim[i]), bz1 = (bz1 && lim[i] >= m), lim[i] = (lim[i] > m ? m : lim[i]);
    	if (bz2){solve(); return 0;}
    	int r;
    	for(re int l = 1; l <= n; l = r + 1)
    	{
    		r = l, col[++cnt] = a[l].c;
    		while (r < n && a[r + 1].c == a[l].c) ++r;
    		if (bz1) r = n;
    		for(re int i = 1; i <= r - l + 1; i++)
    			for(re int j = lim[a[l].c]; j >= a[i + l - 1].w; j--)
    				f[cnt][j] = max(f[cnt][j], f[cnt][j - a[i + l - 1].w] + a[i + l - 1].v);
    	}
    	if (bz1){printf("%lld
    ", f[1][m]); return 0;}
    	for(re int i = 1; i <= cnt; i++)
    		for(re int j = m; j >= 0; j--)
    			for(re int k = 0; k <= min(j, lim[col[i]]); k++)
    				g[j] = max(g[j], g[j - k] + f[i][k]);
    	printf("%lld
    ", g[m]);
    }
    

    ( ext{T2})

    二分答案,考虑如何 (check)
    贪心的想,将 (a) 排序,一一对应要弄出的自然数序列 (b)
    然后就是快速计算代价
    考虑一个位置,前面的代价是 (b_i - a_i) 的形式,后面的是 (a_i - b_i) 的形式(连续性很明显)
    二分这个位置的话是 (O(nlog^2n)) 的,常数优秀就可以过
    实际上这个分割的位置有单调性,指针处理即可

    ( ext{Code})

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #define re register
    using namespace std;
    typedef long long LL;
    
    const int N = 1e6 + 5;
    int n;
    LL num, a[N], sum[N];
    
    inline void read(int &x)
    {
    	x = 0; char ch = getchar(); int f = 1;
    	for(; !isdigit(ch); f = (ch == '-' ? -1 : f), ch = getchar());
    	for(; isdigit(ch); x = (x<<3) + (x<<1) + (ch^48), ch = getchar());
    	x *= f;
    }
    inline LL cost(int l, int r)
    {
    	if (l > r) return 0;
    	return 1LL * (r + l) * (r - l + 1) / 2;
    }
    inline int check(int mid)
    {
    	LL res = num + 1; int r = mid + 1;
    	for(re int i = 1; i <= n - mid; i++)
    	{
    		while (r && r - 1 < a[i + r - 1]) --r;
    		res = min(res, cost(1, r - 1) - (sum[i + r - 1] - sum[i - 1]) + (sum[i + mid] - sum[i + r - 1]) - cost(r, mid));
    	}
    	return res <= num;
    }
    
    int main()
    {
    	freopen("dream.in", "r", stdin), freopen("dream.out", "w", stdout);
    	read(n), scanf("%lld", &num);
    	for(re int i = 1, x; i <= n; i++) read(x), a[i] = x;
    	sort(a + 1, a + n + 1);
    	for(re int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i];
    	int l = 0, r = n - 1, mid, res = -1;
    	while (l <= r)
    	{
    		mid = l + r >> 1;
    		if (check(mid)) res = mid, l = mid + 1;
    		else r = mid - 1;
    	}
    	printf("%lld", res + 1);
    }
    

    ( ext{T3})

    题目理解后就很好做了
    考虑答案的暴力形式,莫反一下

    [ans = frac{sum_{d=1}^k d sum_{t=1}^{lfloor frac{k}{d} floor} mu(t) lfloor frac{k}{dt} floor ^n}{k^n} ]

    发现是个数论分块套数论分块的形式

    ( ext{Code})

    #include <cstdio>
    #define re register
    #define LL long long
    using namespace std;
    
    const int N = 1e6, P = 998244353, inv2 = 499122177;
    int totp, n, k, T, pr[N], vis[N + 5], mu[N + 5], pw[N + 5];
    
    inline int fpow(LL x, LL y)
    {
    	if (x < k && pw[x]) return pw[x];
    	LL res = 1, xx = x;
    	for(; y; y >>= 1, x = x * x % P) if (y & 1) res = res * x % P;
    	if (xx < k) pw[xx] = res;
    	return res;
    }
    
    inline void Euler()
    {
    	vis[1] = mu[1] = 1;
    	for(re int i = 2; i <= N; i++)
    	{
    		if (!vis[i]) pr[++totp] = i, mu[i] = -1;
    		for(re int j = 1; j <= totp && i * pr[j] <= N; j++)
    		{
    			vis[i * pr[j]] = 1;
    			if (!(i % pr[j])) break;
    			mu[i * pr[j]] = -mu[i];
    		}
    	}
    	for(re int i = 1; i <= N; i++) mu[i] += mu[i - 1];
    }
    
    inline LL F(int up)
    {
    	int r; LL sum = 0;
    	for(re int l = 1; l <= up; l = r + 1)
    	{
    		r = up / (up / l);
    		sum = (sum + 1LL * (mu[r] - mu[l - 1] + P) % P * fpow(up / l, n) % P) % P;
    	}
    	return sum;
    }
    
    int main()
    {
    	freopen("dance.in", "r", stdin), freopen("dance.out", "w", stdout);
    	Euler(), scanf("%d", &T);
    	for(; T; --T)
    	{
    		scanf("%d%d", &n, &k);
    		for(re int i = 0; i <= k; i++) pw[i] = 0;
    		LL ans = 0; int r;
    		for(re int l = 1; l <= k; l = r + 1) 
    		{
    			r = k / (k / l);
    			ans = (ans + 1LL * (r + l) * (r - l + 1) % P * inv2 % P * F(k / l) % P) % P;
    		}
    		printf("%lld
    ", ans * fpow(fpow(k, n), P - 2) % P);
    	}
    }
    

    ( ext{T4})

    考虑一个坐标向下走的贡献
    把一个坐标能左右延伸出的位置处理出来,可单调栈做到 (O(n)) 也可懒惰的 (O(n log n)) 二分 + (ST)
    然后把坐标按 (y) 从小到大排序,扫过的点就满足了 (y) 的限制
    先处理当前点可延伸区间的贡献再在树状数组上加入这个点
    非常之简单

    ( ext{Code})

    #include <iostream> 
    #include <cstdio>
    #include <algorithm>
    #define re register
    using namespace std;
    typedef long long LL;
    
    const int N = 5e5 + 5, P = 998244353;
    int n, m, mx[N][21], lg[N];
    struct point{int x, y, w, l, r;}p[N];
    inline bool cmpy(point a, point b){return a.y < b.y;}
    
    inline void read(int &x)
    {
    	x = 0; char ch = getchar(); int f = 1;
    	for(; !isdigit(ch); f = (ch == '-' ? -1 : f), ch = getchar());
    	for(; isdigit(ch); x = (x<<3) + (x<<1) + (ch^48), ch = getchar());
    	x *= f;
    }
    
    inline int query(int l, int r)
    {
    	int k = lg[r - l + 1];
    	return max(mx[l][k], mx[r - (1 << k) + 1][k]);
    }
    inline void prepare()
    {
    	for(re int j = 1; j <= lg[n]; j++)
    		for(re int i = 1; i + (1 << j) - 1 <= n; i++)
    			mx[i][j] = max(mx[i][j - 1], mx[i + (1 << j - 1)][j - 1]);
    	for(re int i = 1; i <= m; i++)
    	{
    		int l = 1, r = p[i].x, mid;
    		while (l <= r)
    		{
    			mid = l + r >> 1;
    			if (query(mid, p[i].x) < p[i].y) p[i].l = mid, r = mid - 1;
    			else l = mid + 1;
    		}
    		l = p[i].x, r = n;
    		while (l <= r)
    		{
    			mid = l + r >> 1;
    			if (query(p[i].x, mid) < p[i].y) p[i].r = mid, l = mid + 1;
    			else r = mid - 1;
    		}
    	}
    }
    
    struct node{LL s0, s1, s2;};
    inline node operator - (const node a, const node b)
    {
    	return node{(a.s0 - b.s0 + P) % P, (a.s1 - b.s1 + P) % P, (a.s2 - b.s2 + P) % P};
    }
    struct BIT{
    	LL c0[N], c1[N], c2[N];
    	inline int lowbit(int x){return x & (-x);}
    	inline void add(int x, LL v)
    	{
    		for(; x <= n; x += lowbit(x))
    			++c0[x], c1[x] = (c1[x] + v) % P, c2[x] = (c2[x] + v * v % P) % P;
    	}
    	inline node getsum(int x)
    	{
    		int s0 = 0, s1 = 0, s2 = 0;
    		for(; x; x -= lowbit(x)) s0 = (s0 + c0[x]) % P, s1 = (s1 + c1[x]) % P, s2 = (s2 + c2[x]) % P;
    		return node{s0, s1, s2};
    	}
    	inline node query(int l, int r){return getsum(r) - getsum(l - 1);}
    }T;
    
    int main()
    {
    	freopen("flame.in", "r", stdin), freopen("flame.out", "w", stdout);
    	read(n), read(m), lg[0] = -1;
    	for(re int i = 1; i <= n; i++) read(mx[i][0]), lg[i] = lg[i >> 1] + 1;
    	for(re int i = 1; i <= m; i++) read(p[i].x), read(p[i].y), read(p[i].w);
    	prepare(), sort(p + 1, p + m + 1, cmpy); node t; LL ans = 0;
    	for(re int i = 1; i <= m; i++)
    	{
    		t = T.query(p[i].l, p[i].r), T.add(p[i].x, p[i].w);
    		ans = (ans + t.s0 * p[i].w % P * p[i].w % P - t.s1 * 2 * p[i].w % P + t.s2 + P) % P;
    	}
    	printf("%lld
    ", ans);
    }
    
  • 相关阅读:
    Instant Python 中文缩减版
    《Java疯狂讲义》(第3版)学习笔记 2
    《Java疯狂讲义》(第3版)学习笔记 1
    NXP Mifare S50标准IC卡- 访问位(Access Bits) 分析
    Python中获取异常(Exception)信息
    支持向量机(SVM)入门
    棋类游戏中人机博弈的设计
    (翻译)如何对python dict 类型按键(keys)或值(values)排序
    Python实现打印二叉树某一层的所有节点
    FIFA halts 2026 bids amid scandal 国际足联在丑闻期间停止2026年足球世界杯申请
  • 原文地址:https://www.cnblogs.com/leiyuanze/p/15534325.html
Copyright © 2020-2023  润新知