zoukankan      html  css  js  c++  java
  • [题解]CSP2019 Solution

    • 至于为什么是 ( ext{Part A}) 而不是 ( ext{Day 1})
    • 那是因为 Day1 T3 还没改
    • (那这六题的 ( ext{solution}) 就按难度顺序写吧)
    • 感觉今年的画风和 ( ext{NOIP 2016}) 有点像?

    D1T1 code

    Solution

    • 直接模拟
    • 如果 (k<2^{n-1}) 就输出 (0)
    • 否则输出 (1) 并把 (k) 变成 (2^n-1-k)
    • 然后 (n) 减掉 (1) 继续进行下去直到 (n=0) 为止
    • 注意 (k) 要开 ( ext{unsigned long long})
    • (O(n))

    Code

    #include <bits/stdc++.h>
    
    typedef unsigned long long ull;
    
    int n;
    ull k;
    
    void solve(int n, ull k)
    {
    	if (!n) return;
    	ull mid = 1ull << n - 1;
    	if (k < mid) putchar('0'), solve(n - 1, k);
    	else putchar('1'), solve(n - 1, mid - 1 - (k - mid));
    }
    
    int main()
    {
    	std::cin >> n >> k;
    	solve(n, k);
    	return puts(""), 0;
    }
    

    D1T2 brackets

    Solution

    • (cnt_u) 表示根到 (u) 的路径组成的括号序列,以 (u) 为右端点的合法括号序列个数
    • 那么 (k_u) 就等于根到 (u) 的路径上所有点的 (cnt) 之和
    • 易得如果存在 (u) 的一个深度最大的祖先 (v) 使得 (v)(u) 的路径组成的括号序列是合法括号序列
    • 那么 (cnt_u=cnt_{fa_v}+1)
    • 对于求这个 (v) ,可以维护一个栈
    • 从根到 (u) ,如果是左括号则直接加入,如果是右括号且栈不空则弹栈
    • 那么如果 (u) 为右括号,那么 (v) 为这次弹出的括号对应的点
    • 而对于求出所有的 (u) ,可以在对树 ( ext{DFS}) 的过程中维护这个栈,在 ( ext{DFS}) 回溯时把栈操作也退回即可
    • (O(n))

    Code

    #include <bits/stdc++.h>
    
    template <class T>
    inline void read(T &res)
    {
    	res = 0; bool bo = 0; char c;
    	while (((c = getchar()) < '0' || c > '9') && c != '-');
    	if (c == '-') bo = 1; else res = c - 48;
    	while ((c = getchar()) >= '0' && c <= '9')
    		res = (res << 3) + (res << 1) + (c - 48);
    	if (bo) res = ~res + 1;
    }
    
    typedef long long ll;
    
    const int N = 5e5 + 5;
    
    int n, fa[N], ecnt, nxt[N], adj[N], go[N], stk[N], top, cnt[N];
    char s[N];
    ll sum[N], ans;
    
    void add_edge(int u, int v)
    {
    	nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
    }
    
    void dfs(int u)
    {
    	int tf = 0;
    	if (s[u] == '(') stk[++top] = u;
    	else if (top) cnt[u] = cnt[fa[tf = stk[top--]]] + 1;
    	sum[u] = sum[fa[u]] + cnt[u];
    	ans ^= sum[u] * u;
    	for (int e = adj[u], v = go[e]; e; e = nxt[e], v = go[e])
    		dfs(v);
    	if (s[u] == '(') top--;
    	else if (tf) stk[++top] = tf;
    }
    
    int main()
    {
    	int x;
    	read(n);
    	scanf("%s", s + 1); n = strlen(s + 1);
    	for (int i = 2; i <= n; i++) read(x), add_edge(fa[i] = x, i);
    	dfs(1);
    	return std::cout << ans << std::endl, 0;
    }
    

    D2T1 meal

    Solution

    • 如果没有一半的限制,那么答案为
    • [prod_{i=1}^n(1+sum_{j=1}^ma_{i,j})-1 ]

    • 而出现次数超过一半的主要食材最多 (1)
    • 故可以枚举超过一半的主要食材是哪种,并把对应的方案数从上式种扣掉即可
    • 假设确定了一种食材 (x) ,考虑如何求这种食材出现超过一半的方案数
    • (u_i=a_{i,x})(v_i=sum_{j=1,j e x}^ma_{i,j})
    • 问题就转化成了有 (n) 个变量,对于第 (i) 个变量有 (u_i) 种方法使其为 (1)(v_i) 种方法使其为 (-1)(1) 种方法使其为 (0),求有多少种方案使得 (1) 的个数严格大于 (-1) (所有变量的和严格大于 (0)
    • 于是可以 ( ext{DP}) :设 (f[i][j]) 表示前 (i) 个变量和为 (j) 的方案数((j) 可以为负),转移时枚举下一个变量的取值
    • (O(mn^2))

    Code

    #include <bits/stdc++.h>
    
    template <class T>
    inline void read(T &res)
    {
    	res = 0; bool bo = 0; char c;
    	while (((c = getchar()) < '0' || c > '9') && c != '-');
    	if (c == '-') bo = 1; else res = c - 48;
    	while ((c = getchar()) >= '0' && c <= '9')
    		res = (res << 3) + (res << 1) + (c - 48);
    	if (bo) res = ~res + 1;
    }
    
    const int N = 105, E = 205, M = 2005, rqy = 998244353;
    
    int n, m, a[N][M], f[N][E], sum[N], tmp[N], ans = 1;
    
    inline void add(int &a, const int &b)
    {
    	a += b; if (a >= rqy) a -= rqy;
    }
    
    inline void sub(int &a, const int &b)
    {
    	a -= b; if (a < 0) a += rqy;
    }
    
    int main()
    {
    	read(n); read(m);
    	for (int i = 1; i <= n; i++) sum[i] = 1;
    	for (int i = 1; i <= n; i++)
    		for (int j = 1; j <= m; j++)
    			read(a[i][j]), add(sum[i], a[i][j]);
    	for (int i = 1; i <= n; i++) ans = 1ll * ans * sum[i] % rqy;
    	sub(ans, 1);
    	for (int i = 1; i <= m; i++)
    	{
    		for (int j = 1; j <= n; j++) tmp[j] = sum[j], sub(tmp[j], a[j][i]), sub(tmp[j], 1);
    		for (int j = -n; j <= n; j++)
    			for (int k = 0; k <= n; k++)
    				f[k][j + n] = 0;
    		f[0][n] = 1;
    		for (int j = 1; j <= n; j++)
    			for (int k = -n; k <= n; k++)
    			{
    				add(f[j][k + n], f[j - 1][k + n]);
    				if (k > -n) add(f[j][k + n], 1ll * f[j - 1][k - 1 + n] * a[j][i] % rqy);
    				if (k < n) add(f[j][k + n], 1ll * f[j - 1][k + 1 + n] * tmp[j] % rqy);
    			}
    		for (int j = 1; j <= n; j++) sub(ans, f[n][j + n]);
    	}
    	return std::cout << ans << std::endl, 0;
    }
    
  • 相关阅读:
    生成二维码
    【C#】教你纯手工用C#实现SSH协议作为GIT服务端
    Git断点续传和离线增量更新的实现
    微信定位真的泄露了你的精确位置
    Helper Files
    正则表达式的一些应用
    Apache配置SSL实现HTTP转HTTPS及可能出现的问题(配置https启动不了的解决办法)
    Python3 采集APP数据及相关配置
    Laravel 5 中间件、路由群组、子域名路由、 权限控制的基本使用方法
    Python3使用cookielib模块
  • 原文地址:https://www.cnblogs.com/xyz32768/p/11906888.html
Copyright © 2011-2022 走看看