• 2020提高组正睿十连测


    (Day2)

    (A)

    注意到排序后每次是选一个前缀,所以可以在线段树上二分,每次全局加上可以拿的物品,选了的清零即可。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    #define ll long long
    
    const int INF = 0x7fffffff;
    const int N = 200000;
    
    int n, m, pos, c[N + 50];
    
    ll sum[N + 50], ans, ssum[N + 50];
    
    void Read(int &x)
    {
    	x = 0; int p = 0; char st = getchar();
    	while (st < '0' || st > '9') p = (st == '-'), st = getchar();
    	while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
    	x = p ? -x : x;
    	return;
    }
    
    struct Node
    {
    	int a, b;
    } stone[N + 50];
    
    struct Tag
    {
    	ll x, y;
    };
    
    struct Sgementtree
    {
    	Tag tag[(N << 2) + 50], tree[(N << 2) + 50];
    	long long sl[(N << 2) + 50], s[(N << 2) + 50];
    	void Add1(int k, int l, int r, int add)
    	{
    		s[k] += 1LL * add * (ssum[r] - ssum[l - 1]); sl[k] += 1LL * add * (sum[r] - sum[l - 1]);
    		tag[k].x += add;
    		return;
    	}
    	void Ql(int k)
    	{
    		s[k] = sl[k] = tag[k].x = 0; tag[k].y = 1;
    		return;
    	}
    	void Pushdown(int k, int l, int r, int mid)
    	{
    		if (tag[k].y)
    			Ql(k << 1), Ql(k << 1 | 1), tag[k].y = 0;
    		if (tag[k].x)
    			Add1(k << 1, l, mid, tag[k].x), Add1(k << 1 | 1, mid + 1, r, tag[k].x), tag[k].x = 0;
    		return;
    	}
    	void Pushup(int k)
    	{
    		s[k] = s[k << 1] + s[k << 1 | 1];
    		sl[k] = sl[k << 1] + sl[k << 1 | 1]; 
    		return;
    	}
    	ll Query(int k, int l, int r, int ask)
    	{
    		if (l == r)
    		{
    			tag[k].x = tag[k].y = 0;
    			s[k] -= 1LL * ask * stone[l].a;
    			sl[k] -= ask;
    			return 1LL * ask * stone[l].a;
    		}
    		int mid = (l + r) >> 1;
    		Pushdown(k, l, r, mid);
    		if (sl[k << 1] < ask) 
    		{
    			ask -= sl[k << 1];
    			ll tmp = s[k << 1];
    			Ql(k << 1);
    			tmp += Query(k << 1 | 1, mid + 1, r, ask);
    			Pushup(k);
    			return tmp;
    		}
    		else 
    		{
    			ll tmp = Query(k << 1, l, mid, ask);
    			Pushup(k);
    			return tmp;
    		}
    	}
    } tr;
    
    int Cmp(Node a, Node b)
    {
    	return a.a < b.a;
    }
    
    int main()
    {
    	Read(n); Read(m);
    	for (int i = 1; i <= n; i++) Read(c[i]);
    	for (int i = 1; i <= m; i++) Read(stone[i].a), Read(stone[i].b);
    	sort(stone + 1, stone + m + 1, Cmp);
    	for (int i = 1; i <= m; i++)
    		if (stone[i].b == -1)
    		{
    			pos = i; 
    			break;
    		}
    	for (int i = 1; i <= pos - 1; i++) sum[i] = sum[i - 1] + stone[i].b, ssum[i] = ssum[i - 1] + 1LL * stone[i].a * stone[i].b;
    	sum[pos] = sum[pos - 1] + 1000001;
    	for (int i = 1; i <= n; i++)
    	{
    		tr.Add1(1, 1, pos, 1);
    		ans += tr.Query(1, 1, pos, c[i]);
    	}
    	cout << ans;
    	return 0; 
    }
    

    (B)

    经典结论只需重载(sort)中的(cmp)函数。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    
    using namespace std;
    
    const int N = 300000;
    
    string st[N + 50];
    
    int n;
    
    int Cmp(string a, string b)
    {
    	return a + b < b + a;
    }
    
    int main()
    {
    	scanf("%d", &n);
    	for (int i = 1; i <= n; i++) cin >> st[i];
    	sort(st + 1, st + n + 1, Cmp);
    	for (int i = 1; i <= n; i++) cout << st[i];
    	return 0;
    }
    

    (C)

    推一下结论,发现题目转化为找一个权值最大的向量集合使得(mod 3)下线性无关,用线性基维护,注意要用位运算优化三进制操作。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    #define ll long long
    
    const int N = 500050;
    
    ll lastans, ans;
    
    int n;
    
    struct Node
    {
    	ll pos1, pos2; int zhi; 
    } a[60];
    
    template<class T>
    void Read(T &x)
    {
    	x = 0; int p = 0; char st = getchar();
    	while (st < '0' || st > '9') p = (st == '-'), st = getchar();
    	while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
    	x = p ? -x : x;
    	return; 
    }
    
    void Add(Node &a, const Node &b)
    {
    	ll a0 = ~(a.pos1 | a.pos2), b0 = ~(b.pos1 | b.pos2);
    	a = (Node){(a.pos2 & b.pos2) | (a.pos1 & b0) | (a0 & b.pos1), (a.pos1 & b.pos1) | (a.pos2 & b0) | (a0 & b.pos2), a.zhi};
    	return;
    }
    
    void Del(Node &a, const Node &b)
    {
    	ll a0 = ~(a.pos1 | a.pos2), b0 = ~(b.pos1 | b.pos2);
    	a = (Node){(a.pos2 & b.pos1) | (a.pos1 & b0) | (a0 & b.pos2), (a0 & b.pos1) | (a.pos2 & b0) | (a.pos1 & b.pos2), a.zhi};
    	return;
    }
    
    void Ins(Node tmp)
    {
    	for (ll i = 59; i >= 0; i--)
    		if (((tmp.pos1 >> i) & 1) | ((tmp.pos2 >> i) & 1))
    		{
    			if (!a[i].zhi) 
    			{
    				a[i] = tmp; ans += tmp.zhi;
    				return;
    			}
    			if (tmp.zhi > a[i].zhi)
    			{
    				ans += tmp.zhi - a[i].zhi;
    				swap(tmp, a[i]);
    			}
    			if (((tmp.pos1 >> i & 1) & (a[i].pos1 >> i & 1)) | ((tmp.pos2 >> i & 1) & (a[i].pos2 >> i & 1))) Del(tmp, a[i]);
    			else Add(tmp, a[i]);
    		}
    	return;
    }
    
    int main()
    {
    	Read(n);
    	ll x; int y;
    	for (int i = 1; i <= n; i++)
    	{
    		Read(x); Read(y);
    		x = x ^ lastans;
    		Ins((Node){x, 0, y});
    		lastans = ans; printf("%lld
    ", ans);
    	}
    	return 0;
    }
    

    (Day3)

    (A)

    每个点随机为工业城市或农业城市,考虑期望的线性性,一条边对答案的贡献是(frac{1}{2}),那么总的期望好边数就是(frac{m}{2}),那么必然有构造方式可以构造出一种方案使得变数(> frac{m}{2})

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    int n, m;
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	printf(m == 0 ? "No
    " : "Yes
    ");
    } 
    

    (B)

    发现操作可逆,于是可以将两个序列都转化成一个中间序列,如果可行则有解。

    可以将中间序列定为能变成的字典序最小的序列,这里的字典序最小指在(1)的个数尽可能少的情况下(1)的位置尽量靠前。

    题目中所给的操作等价于如果一个(1)前面有(k)(0),那么它的位置可以往前移(k),如果有连续(k)(1),可以都变为(0)

    这样子发现移动之后的位置(mod k)不变,所以可以根据此性质进行模拟。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    const int N = 1000000;
    
    int a[N + 50], b[N + 50], lena, lenb, n, k;
    
    char sta[N + 50], stb[N + 50];
    
    void Calc(char *st, int *a, int &len)
    {
    	for (int i = 1; i <= n; i++)
    	{
    		if (st[i] == '0') continue;
    		if (!len) { a[++len] = i % k; continue; }
    		int u = i % k, lst = a[len];
    		if (u <= lst) u += ((lst - u) / k + 1) * k;
    		a[++len] = u;
    		while (len >= k && a[len] - k + 1 == a[len - k + 1]) len -= k;
    	}
    	while (len >= k && a[len] - k + 1 == a[len - k + 1]) len -= k;
    	return;
    }
    
    int main()
    {
    	int t;
    	scanf("%d", &t);
    	while (t--)
    	{
    		lena = lenb = 0;
    		scanf("%d%d", &n, &k);
    		scanf("%s", sta + 1);
    		Calc(sta, a, lena);
    		scanf("%s", stb + 1);
    		Calc(stb, b, lenb);
    		if (lena != lenb) { puts("No"); continue; };
    		int flag = 0;
    		for (int i = 1; i <= lena; i++) if (a[i] != b[i]) { puts("No"); flag = 1; break; }
    		if (!flag) puts("Yes"); 
    	}
    	return 0;
    }
    

    (C)

    发现可能的最大的序列的差值(d)是将所有必定在集合里的元素排序后两两之间的差的(gcd)

    设必须出现的最小元素为(l),最大为(r),然后在([l,r])中的不能出现的元素的位置为(pos),那么(pos - l)的所有因子都不满足条件。

    这样找出了所有可能作差的值,剩下就是看能向左向右延伸多远,发现还是拿(pos)去更新某个数的因子,这玩意可以高维前缀和,懒得写就直接暴力记录每个数的因子了。

    #include <iostream>
    #include <cstdio>
    #include <vector>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    
    using namespace std;
    
    #define ll long long
    
    const int INF = 0x7fffffff;
    const int MOD = 1000000007;
    const int M = 1000000;
    
    ll n, b[M + 50], minn[M + 50], maxx[M + 50], L, R;
    
    int m, p[M + 50], prime[M + 50], primecnt, cnt;
    
    vector<ll> ones, zeros;
    vector<int> g[M + 50];
    
    template <class T>
    void Read(T &x)
    {
    	x = 0; int p = 0; char st = getchar();
    	while (st < '0' || st > '9') p = (st == '-'), st = getchar();
    	while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
    	x = p ? -x : x;
    	return; 
    }
    
    ll Gcd(ll a, ll b)
    {
    	return a % b == 0 ? b : Gcd(b, a % b);
    }
    
    void Prework()
    {
    	p[0] = p[1] = 1;
    	for (int i = 2; i <= M; i++)
    	{
    		if (!p[i]) prime[++primecnt] = i; 
    		for (int j = 1; j <= primecnt && prime[j] * i <= M; j++)
    		{
    			p[prime[j] * i] = 1;
    			if (i % prime[j] == 0) break;
    		}
    	}
    	return;
    }
    
    ll Abs(ll x)
    {
    	return x < 0 ? -x : x;
    }
    
    int Solve(ll d)
    {
    /*	ll s = b[d];
    	for (int i = 1; i <= primecnt; i++)
    	{
    		ll tmp = 1;
    		while (s % prime[i] == 0)
    		{
    			tmp *= prime[i];
    			int pos = lower_bound(b + 1, b + cnt + 1, tmp) - b;
    			minn[pos] = max(minn[pos], minn[d]);
    			maxx[pos] = min(maxx[pos], maxx[d]);
    			s /= prime[i];
    		}
    	}
    	if (s)
    	{
    		int pos = lower_bound(b + 1, b + cnt + 1, s) - b;
    		minn[pos] = max(minn[pos], minn[d]);
    		maxx[pos] = min(maxx[pos], maxx[d]);
    	}*/
    //	cout << b[d] << " " << minn[d] << " " << maxx[d] << endl;
    	if (minn[d] > maxx[d]) return 0;
    	return 1LL * ((maxx[d] - R) / b[d] + 1) * ((L - minn[d]) / b[d] + 1) % MOD;
    }
    
    int main()
    {
    	Prework();
    	Read(n); Read(m);
    	int opt; ll x;
    	for (int i = 1; i <= m; i++)
    	{
    		Read(opt); Read(x);
    		if (opt) ones.push_back(x); else zeros.push_back(x);
    	}
    	sort(ones.begin(), ones.end());
    	ll maxd = ones[1] - ones[0];
    	for (int i = 2; i < ones.size(); i++) maxd = Gcd(maxd, ones[i] - ones[i - 1]);
    	L = ones[0], R = ones[ones.size() - 1]; 
    	ll d = maxd;
    //	cout << maxd << endl;
    	for (ll i = 1; i <= sqrt(maxd); i++)
    		if (maxd % i == 0)
    		{
    			b[++cnt] = i;
    			if (maxd / i != i) b[++cnt] = maxd / i;
    		}
    	sort(b + 1, b + cnt + 1);
    	for (int i = 1; i <= cnt; i++)
    		for (int j = 1; j <= i; j++)
    			if (b[i] % b[j] == 0)
    				g[i].push_back(j);
    	for (int i = 1; i <= cnt; i++) minn[i] = 1, maxx[i] = n/*, cout << i << " " << b[i] << endl*/;
    	for (vector<ll>::iterator it = zeros.begin(); it != zeros.end(); it++)
    	{
    		ll v = *it;
    		if (v >= L && v <= R) 
    		{
    			ll tmp = Gcd(v - L, maxd);
    			int pos = lower_bound(b + 1, b + cnt + 1, tmp) - b;
    			for (vector<int>::iterator it = g[pos].begin(); it != g[pos].end(); it++)
    			{
    				int u = *it;
    				minn[u] = INF; maxx[u] = -INF;
    			}
    		}
    		else if (v < L)
    		{
    			ll tmp = Gcd(L - v, maxd);
    			int pos = lower_bound(b + 1, b + cnt + 1, tmp) - b;
    			for (vector<int>::iterator it = g[pos].begin(); it != g[pos].end(); it++)
    			{
    				int u = *it;
    				minn[u] = max(minn[u], v + 1);
    			}
    		}
    		else if (v > R)
    		{
    			ll tmp = Gcd(v - L, maxd);
    			int pos = lower_bound(b + 1, b + cnt + 1, tmp) - b;
    			for (vector<int>::iterator it = g[pos].begin(); it != g[pos].end(); it++)
    			{
    				int u = *it;
    				maxx[u] = min(maxx[u], v - 1);
    			}
    		}
    	}
    	int ans = 0;
    	for (int i = cnt; i >= 1; i--) ans = (ans + Solve(i)) % MOD/*, cout << Solve(i) << endl*/;
    	printf("%d", ans);
    	return 0;
    }
    

    (Day5)

    (A)

    这题结论(10min)就想出来了,但是题意读假了,以为必然连通。

    直接粘题解吧,题解多详细(

    首先考虑非空连通图的情况。题目中描述的是欧拉回路,根据相关知识,我们知道可行当且仅当每个点
    度数均为偶数。那么问题转化为判断 阶线图是否每个点度数均为偶数。

    假设(k >= 1) ,注意到(k)阶线图非空且每个点度数为偶数的话,意味着(k - 1)阶线图非空且每个点度数奇偶
    性相同。再考虑(k)阶线图非空且每个点度数均为奇数的条件,当(k >= 1)时,这意味着(k - 1)阶线图每条边
    两端节点度数奇偶性不同,也即,(k - 1)阶线图是一个非空的二分图,且每条边两端节点度数奇偶性不
    同。可以发现这样的图如果不是(K_{1,2})的话,一定存在度数(> 2)的节点但不存在三元环,因此不可能是任
    意图的线图(否则考虑原图必然有度数$ > 2$的节点,因此它的线图会存在三元环,矛盾)。

    这样我们就得到了一个非空连通图的判定算法:对于(k = 0)的情况,必定是每个点度数均为偶数;对于(k >= 1)
    的情况,还允许每个点度数均为奇数;对于(k >= 2)的情况,还允许是一个二分图且每条边两端节点
    度数奇偶性不同。

    但其实这个算法仍然有很多漏洞:关键在于对于一个“链”状的连通块,它经过若干次求线图操作后会变
    成单点甚至空图!因此我们还需要特殊考虑链状的连通块是否会变成单点或者空图,甚至即使有多个连
    通块,也可能是有解的,需要进一步的讨论,这里就不展开了。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    
    const int INF = 0x7fffffff; 
    const int N = 1000000;
    
    using namespace std;
    
    int n, m, k, fa[N + 50], ds[N + 50], p[N + 50], num[N + 50], xjds[N + 50], hh, tmpans, lian;
    
    vector<int> pos[N + 50];
    
    struct Bian
    {
    	int u, v;	
    } edge[N + 50];
    
    void Read(int &x)
    {
    	x = 0; int p = 0; char st = getchar();
    	while (st < '0' || st > '9') p = (st == '-'), st = getchar();
    	while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
    	x = p ? -x : x;
    	return; 
    }
    
    int Find(int x)
    {
    	return fa[x] == x ? fa[x] : fa[x] = Find(fa[x]);
    } 
    
    int Pd(int id)
    {
    	if (num[id] != pos[id].size() - 1) return 0;
    	for (vector<int>::iterator it = pos[id].begin(); it != pos[id].end(); it++)
    	{
    		int v = *it;
    		if (ds[v] > 2) return 0;
    	}
    	return 1;
    }
    
    int Solve(int id)
    {
    	int ods = 1;
    	for (vector<int>::iterator it = pos[id].begin(); it != pos[id].end(); it++)
    	{
    		int v = *it;
    		if (ds[v] & 1) { ods = 0; break; }
    	}
    	if (ods) return 0;
    	int jds = 1;
    	for (vector<int>::iterator it = pos[id].begin(); it != pos[id].end(); it++)
    	{
    		int v = *it;
    		if (!(ds[v] & 1)) { jds = 0; break; }
    	}
    	if (jds) return 1;	
    	if (xjds[id]) return 2;
    	return INF;
    }
    
    int main()
    {
    	Read(n); Read(m); Read(k);
    	if (m == 0) { puts("No"); return 0; }
    	if (!n) { puts("No"); return 0; }
    	for (int i = 1; i <= n; i++) fa[i] = i;
    	for (int i = 1; i <= m; i++) 
    	{
    		Read(edge[i].u); Read(edge[i].v);
    		fa[Find(edge[i].u)] = Find(edge[i].v);
    		ds[edge[i].u]++; ds[edge[i].v]++;
    	}
    	for (int i = 1; i <= n; i++) fa[i] = Find(i), pos[fa[i]].push_back(i);
    	for (int i = 1; i <= m; i++) num[fa[edge[i].u]]++;
    	for (int i = 1; i <= n; i++) 
    		if (!p[fa[i]])
    		{
    			if (Pd(fa[i])) 
    			{
    				p[fa[i]] = 2;
    				if (pos[fa[i]].size() - 1 == tmpans) lian++;
    				if (pos[fa[i]].size() - 1 > tmpans) lian = 1, tmpans = pos[fa[i]].size() - 1;
    			}
    			else p[fa[i]] = 1;	
    		} 
    	for (int i = 1; i <= n; i++) xjds[i] = 1;
    	for (int i = 1; i <= m; i++)
    		if ((ds[edge[i].u] & 1) == (ds[edge[i].v] & 1)) 
    			xjds[fa[edge[i].u]] = 0;
    	int tmpans2 = 0;
    	for (int i = 1; i <= n; i++)
    		if (p[fa[i]] == 1)
    		{
    			hh++;
    			if (hh > 1) { puts("No"); return 0; }
    			p[fa[i]] = 0;
    			tmpans2 = Solve(fa[i]);
    		} 
    	if (!hh && lian > 1) { puts("No"); return 0; }
    	if (hh && lian) tmpans++;
    	tmpans = max(tmpans, tmpans2);
    	if (!hh && tmpans != k) { puts("No"); return 0; }
    	if (tmpans > k) { puts("No"); return 0; }
    	puts("Yes");
    	return 0;
    }
    
  • 相关阅读:
    100 余个网页设计优化案例(用户体验、交互优化等方面)
    Tinyhttpd 源代码初步解读
    emlog pro 文章编辑器(editor.md)的快捷键
    什么是 CSS 设计模式
    原生 JS 实现 HTML 转 Markdown,以及其实现逻辑(html2md.js 或 html2markdown.js)
    【Example】C++ 回调函数及 std::function 与 std::bind
    【Example】C++运算符重载
    【小记】Linux find 配合 rm 命令安全批量删除文件
    【小记】Linux 快速查找并结束僵尸进程
    【Example】C++ 标准库多线程同步及数据共享 (std::future 与 std::promise)
  • 原文地址:https://www.cnblogs.com/Tian-Xing-Sakura/p/13622047.html
Copyright © 2020-2023  润新知