• Codeforces Round #773 (Div. 1) A~D 题解


    比赛链接

    A

    开个 map,能选掉就选掉。

    #include <bits/stdc++.h>
    #define DC int T = gi <int> (); while (T--)
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define Add(a, b) a = (a + b) % mod
    #define Mul(a, b) a = 1ll * a * b % mod
    #define chkmin(a, b) a = min(a, b)
    #define chkmax(a, b) a = max(a, b)
    #define int long long
    
    using namespace std;
    
    typedef long long LL;
    typedef unsigned long long ULL;
    typedef pair <int, int> PII;
    typedef pair <LL, int> PLI;
    typedef pair <LL, LL> PLL;
    
    template <typename T>
    inline T gi()
    {
    	T x = 0, f = 1; char c = getchar();
    	while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
    	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return f * x;
    }
    
    const int N = 200003, M = N << 1;
    
    int n, x, a[N];
    map <int, int> p;
    
    inline void solve()
    {
    	n = gi <int> (), x = gi <int> ();
    	p.clear();
    	for (int i = 1; i <= n; i+=1) a[i] = gi <int> (), ++p[a[i]];
    	for (auto &y : p)
    		if (y.se)
    		{
    			int now = y.fi * x;
    			int mn = min(p[now], y.se);
    			y.se -= mn;
    			p[now] -= mn;
    		}
    	int sum = 0;
    	for (auto y : p) sum += y.se;
    	cout << sum << endl;
    	return;
    }
    
    signed main()
    {
    	//freopen(".in", "r", stdin); freopen(".out", "w", stdout);
    	DC solve();
    	return !!0;
    }
    

    B

    阴间构造

    对于这种构造,我们肯定是要先找到一种基本操作,然后根据基本操作去构造方案。

    不难发现无解的必要条件是存在数字出现了奇数次。下面我们通过构造方案证明其他情况下一定有解。

    基本操作:翻转一段区间,即 abc \(\to\) cba

    操作如下:

    \[\text{abc}\\ \text{abc}\underline{\textbf{aa}}\\ \text{abca}\underline{\textbf{bb}}\text{a}\\ \text{abcab}\underline{\textbf{cc}}\text{ba}\\ \text{[abcabc]cba} \]

    从前往后扫描序列,将相邻两个相同字符的位置配对,假设其中一对是 \((i,j)\)

    那么我们可以通过依次翻转 \((i,j-1)\)\((i,j)\)\(j\) 移动到 \(i\) 之后且其他字符相对顺序不变。

    举个例子:假设序列为 \(\text{abca}\)

    操作如下:

    \[\text{abca}\\ \text{abc}\underline{\textbf{aa}}\text{a}\\ \text{abca}\underline{\textbf{bb}}\text{aa}\\ \text{abcab}\underline{\textbf{cc}}\text{baa}\\ \text{abcabccbaa}\underline{\textbf{cc}}\\ \text{abcabccbaac}\underline{\textbf{bb}}\text{c}\\ \text{abcabccbaacb}\underline{\textbf{aa}}\text{bc}\\ \text{abcabccbaacba}\underline{\textbf{aa}}\text{abc}\\ \text{[abcabc][cbaacbaa][aa]bc} \]

    代码实现(可能?)需要一定的技巧性。

    具体的,每做完一次操作就将已经操作过的数移到最前面,对剩余数重新计算匹配位置,顺便记录一下当前移动到的位置。

    参考代码:

    #include <bits/stdc++.h>
    #define DC int T = gi <int> (); while (T--)
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define Add(a, b) a = (a + b) % mod
    #define Mul(a, b) a = 1ll * a * b % mod
    #define chkmin(a, b) a = min(a, b)
    #define chkmax(a, b) a = max(a, b)
    
    using namespace std;
    
    typedef long long LL;
    typedef unsigned long long ULL;
    typedef pair <int, int> PII;
    typedef pair <LL, int> PLI;
    typedef pair <LL, LL> PLL;
    
    template <typename T>
    inline T gi()
    {
    	T x = 0, f = 1; char c = getchar();
    	while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
    	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return f * x;
    }
    
    const int INF = 0x3f3f3f3f, N = 503, M = N << 1;
    
    int n, now, a[N], c[N], cnt, p[N], t[N];
    vector <int> b;
    int pp[N], pre[N];
    vector <PII> ans;
    vector <int> lens;
    bool vis[N];
    
    inline void filp(int l, int st, int len, int op)
    {
    	if (len < 2) return;
    	now += len;
    	if (!op)
    		for (int i = 1; i <= len; i+=1)
    			ans.pb({now++, c[a[st + i - 1]]});
    	else
    	{
    		for (int i = len - 1; i >= 1; i-=1)
    			ans.pb({now++, c[a[st + i - 1]]});
    		ans.pb({now++, c[a[st + len - 1]]});
    	}
    	lens.pb(len << 1);
    }
    
    inline void rebuild()
    {
    	int qq = 0;
    	for (int i = 1; i <= n; i+=1) if (vis[i]) t[++qq] = a[i];
    	int tmp = qq;
    	for (int i = 1; i <= n; i+=1) if (!vis[i]) t[++qq] = a[i];
    	for (int i = 1; i <= n; i+=1) a[i] = t[i], pre[i] = pp[i] = vis[i] = 0;
    	for (int i = 1; i <= tmp; i+=1) vis[i] = 1;
    	for (int i = tmp + 1; i <= n; i+=1)
    		if (!pre[a[i]]) pre[a[i]] = i;
    		else pp[pre[a[i]]] = i, pre[a[i]] = 0;
    }
    
    inline void solve()
    {
    	n = gi <int> ();
    	b.clear(), ans.clear(), lens.clear();
    	for (int i = 1; i <= n; i+=1) pre[i] = p[i] = vis[i] = 0;
    	for (int i = 1; i <= n; i+=1) a[i] = c[i] = gi <int> ();
    	sort(c + 1, c + 1 + n); cnt = unique(c + 1, c + 1 + n) - c - 1;
    	for (int i = 1; i <= n; i+=1) a[i] = lower_bound(c + 1, c + 1 + cnt, a[i]) - c, ++p[a[i]];
    	for (int i = 1; i <= n; i+=1) if (p[i] & 1) return (void)puts("-1");
    	for (int i = 1; i <= n; i+=1)
    		if (!pre[a[i]]) pre[a[i]] = i;
    		else pp[pre[a[i]]] = i, pre[a[i]] = 0;
    	now = 0;
    	for (int i = 1; i <= n; i+=2)
    		filp(now, i, pp[i] - i, 0), filp(now, i, pp[i] - i + 1, 1), lens.pb(2), now += 2, vis[i] = vis[pp[i]] = 1, rebuild();
    	cout << ans.size() << endl;
    	for (auto x : ans) cout << x.fi << ' ' << x.se << endl;
    	cout << lens.size() << endl;
    	for (auto x : lens) cout << x << ' ';
    	puts("");
    	return;
    }
    
    int main()
    {
    //	freopen(".in", "r", stdin); freopen(".out", "w", stdout);
    	DC solve();
    	return 0;
    }
    

    C

    容易发现如果一个询问的答案是 YES,就相当于有一个区间只有这一个位置没有被覆盖成 \(0\)

    经过转化后的题意:

    • 若操作为 0 l r 0,相当于将 \([l,r]\) 全都覆盖成 \(0\)
    • 若操作为 0 l r 1,相当于加入一条线段 \([l,r]\)
    • 若操作为 1 x,算出以 \(x-1\) 为右端点的最长的一个为 \(0\) 的段的左端点 \(l_1\),和以 \(x+1\) 为右端点的最长的一个段为 \(0\) 的右端点 \(r_1\),相当于询问有没有一条线段 \([l,r]\) 使得 \(l_1\le l\le x\land x\le r\le r_1\)

    线段树二分之后离线差分变成三维偏序,CDQ 分治即可。

    上面那句话也就图一乐。

    考虑并查集 + set 启发式合并,每次覆盖就把 \([l-1,r]\) 的集合并起来,加入线段就直接在 \(r\) 所在的集合加入点 \(l\),查询直接看在 \(x\) 所在集合有没有点在 \([l_1,x]\) 之间,\(l_1\) 也可以并查集算。

    注意 set 启发式合并的时候直接交换两个 set 即可。

    #include <bits/stdc++.h>
    #define DC int T = gi <int> (); while (T--)
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define Add(a, b) a = (a + b) % mod
    #define Mul(a, b) a = 1ll * a * b % mod
    #define chkmin(a, b) a = min(a, b)
    #define chkmax(a, b) a = max(a, b)
    
    using namespace std;
    
    typedef long long LL;
    typedef unsigned long long ULL;
    typedef pair <int, int> PII;
    typedef pair <LL, int> PLI;
    typedef pair <LL, LL> PLL;
    
    template <typename T>
    inline T gi()
    {
    	T x = 0, f = 1; char c = getchar();
    	while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
    	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return f * x;
    }
    
    const int INF = 0x3f3f3f3f, N = 200003, M = N << 1;
    
    int n, q;
    int fa[N], L[N];
    set <int> s[N];
    
    int getf(int u) {return fa[u] == u ? u : fa[u] = getf(fa[u]);}
    
    inline void Union(int x, int y)
    {
    	int fx = getf(x), fy = getf(y);
    	if (fx != fy)
    	{
    		fa[fx] = fy;
    		chkmin(L[fy], L[fx]);
    		if (s[fx].size() > s[fy].size()) swap(s[fx], s[fy]);
    		for (auto p : s[fx]) s[fy].insert(p);
    		set <int> ().swap(s[fx]);
    	}
    }
    
    int main()
    {
    //	freopen(".in", "r", stdin); freopen(".out", "w", stdout);
    	n = gi <int> (), q = gi <int> ();
    	iota(fa + 1, fa + 2 + n, 1);
    	iota(L + 1, L + 2 + n, 1);
    	while (q--)
    	{
    		int op = gi <int> ();
    		if (!op)
    		{
    			int l = gi <int> (), r = gi <int> (), ty = gi <int> ();
    			if (!ty)
    				for (int i = getf(r); i >= l; )
    					Union(i, i - 1), i = getf(i);
    			else s[getf(r)].insert(l);
    		}
    		else
    		{
    			int u = gi <int> ();
    			if (L[getf(u)] != u) puts("NO");
    			else
    			{
    				int fu = L[getf(u - 1)], fv = getf(u);
    				auto x = s[fv].lower_bound(fu + 1);
    				if (x != s[fv].end() && (*x) <= u) puts("YES");
    				else puts("N/A");
    			}
    		}
    	}
    	return 0;
    }
    

    D

    核心:两个序列 \(\{a\}\)\(\{b\}\) 没有相同元素的判断方法:记一个计数器 \(cnt\),对于每一个 \(b\) 的子集,如果它的元素个数是奇数,就将 \(cnt\) 加上它在 \(a\) 中的出现次数,否则就减去它在 \(a\) 中的出现次数。最后如果 \(cnt=1\) 说明 \(a\)\(b\) 至少有一个元素相同,否则它们就没有相同元素。

    有这个结论之后对每一个子集哈希,双指针求答案即可。

    #include <bits/stdc++.h>
    #define DC int T = gi <int> (); while (T--)
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define Add(a, b) a = (a + b) % mod
    #define Mul(a, b) a = 1ll * a * b % mod
    #define chkmin(a, b) a = min(a, b)
    #define chkmax(a, b) a = max(a, b)
    
    using namespace std;
    
    typedef long long LL;
    typedef unsigned long long ULL;
    typedef pair <int, int> PII;
    typedef pair <LL, int> PLI;
    typedef pair <LL, LL> PLL;
    
    template <typename T>
    inline T gi()
    {
    	T x = 0, f = 1; char c = getchar();
    	while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
    	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return f * x;
    }
    
    const int INF = 0x3f3f3f3f, N = 100003, M = N << 1;
    const ULL base = 11451419198101ull;
    
    unordered_map <ULL, int> p;
    int n, m;
    struct Node
    {
    	int a[6], w;
    	ULL b[33];
    } a[N];
    
    inline void modify(int id, int op) {for (int i = 1; i < (1 << m); i+=1) p[a[id].b[i]] += op;}
    
    inline int query(int id)
    {
    	int now = 0;
    	for (int i = 1; i < (1 << m); i+=1)
    		if (__builtin_parity(i)) now += p[a[id].b[i]];
    		else now -= p[a[id].b[i]];
    	return now;
    }
    
    int main()
    {
    //	freopen(".in", "r", stdin); freopen(".out", "w", stdout);
    	n = gi <int> (), m = gi <int> ();
    	for (int i = 1; i <= n; i+=1)
    	{
    		for (int j = 0; j < m; j+=1)
    			a[i].a[j] = gi <int> ();
    		sort(a[i].a, a[i].a + m);
    		for (int j = 1; j < (1 << m); j+=1)
    			for (int k = 0; k < m; k+=1)
    				if (j >> k & 1)
    					a[i].b[j] = a[i].b[j] * base + a[i].a[k];
    		a[i].w = gi <int> ();
    	}
    	sort(a + 1, a + 1 + n, [](Node x, Node y) {return x.w < y.w;});
    	for (int i = 1; i <= n; i+=1) modify(i, 1);
    	int r = n, ans = 2000000007;
    	for (int l = 1; l <= n; l+=1)
    	{
    		bool fl = 0;
    		modify(l, -1);
    		while (query(l) < r - l) modify(r--, -1), fl = 1;
    		if (fl) modify(++r, 1), chkmin(ans, a[l].w + a[r].w);
    	}
    	if (ans != 2000000007) printf("%d\n", ans);
    	else puts("-1");
    	return 0;
    }
    
  • 相关阅读:
    2.java基础语法(上)
    1.java概述
    Qt layout透明的问题
    Duilib 关于ChildLayout崩溃的问题
    关于注册表使用的几个问题
    win32接口获取ping值
    Web开发中遇到的问题
    DuiLib 窗口透明方法
    通过进程名杀死进程的方法--WIN32
    关于在Qt的MainWindow窗口中添加Layout的问题
  • 原文地址:https://www.cnblogs.com/xsl19/p/cf1641_sol.html
Copyright © 2020-2023  润新知