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\n", 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\n" : "Yes\n");
} 

\(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;
}
posted @ 2020-09-06 16:11  Tian-Xing  阅读(338)  评论(0编辑  收藏  举报