2021暑假多校(HD+牛客)补题

2021“MINIEYE杯”中国大学生算法设计超级联赛(1)


6950.Mod, Or and Everything

题意:给你一个数字 n n n,让你求 n % 1 ∣ n % 2 ∣ n % 3 ∣ ⋅ ⋅ ⋅ ∣ n % n n\%1|n\%2|n\%3|···|n\%n n%1n%2n%3n%n
水题,打表会发现一个显而易见的规律

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e6 + 10;
const ll mod = 1e9 + 7;
const ll inf = 0x3f3f3f3f3f3f3f3f;

ll T, n;
ll vis[maxn];
vector<ll> dd;
vector<ll> cc;
void init() {
	ll temp = 0;
	ll num = 1;
	ll c = 0;
	while (temp <= 1e12) {
		temp += num;
		num *= 2;
		dd.emplace_back(temp);
		cc.emplace_back(c);

		c = 2 * c + 1;
	}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	init();

	int T;
	cin >> T;
	while (T--) {
		cin >> n;
		if (n <= 2) {
			cout << 0 << "\n";
			continue;
		}
		else n--;

		ll temp = lower_bound(dd.begin(), dd.end(), n) - dd.begin();
		cout << cc[temp] << "\n";
	}

	return 0;
}





6954.Minimum spanning tree

题意:给你 n − 1 n-1 n1个点,从 2 − n 2-n 2n编号,任意两点之间的边权为 l c m ( a , b ) lcm(a,b) lcm(a,b) a , b a,b ab是点编号。让你构造一棵最小生成树,输出树的权值。

对于一个点,他与其他点创造的贡献值,最小的可能就是他自己本身的编号,我们尽可能的利用这一点。
比如,

2-4-6-8-10·····
3-6-9-12-15····
5-10-15-20····

我们把每一个素数以及他构成的合数连在一起,那么他们一团的权值就是最小的(合数之和)
但是会有重复的比如6,那么我们就把他连在某一个串里就行,并不影响最后的结果
所以答案的一部分就是 ≤ n \leq n n的合数之和
对于不同的素数开头的串,把他们连接起来,最小的代价当然是都连在 2 2 2上, 2 − 3 , 2 − 5 2-3,2-5 23,25这样连起来,所以
a n s = ∑ i = 2 , i 是 合 数 n i + ∑ j = 2 , j 是 素 数 n 2 ∗ j ans=\sum_{i=2,i是合数}^ni+\sum_{j=2,j是素数}^n2*j ans=i=2,ini+j=2,jn2j

那么我们可以欧拉筛筛出素数,再前缀和一下

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e7 + 10;
const ll mod = 1e9 + 7;
const ll inf = 0x3f3f3f3f3f3f3f3f;
bool vis[maxn];
int prime[maxn];
int cnt = 0;
ll n;
void Euler_prime() {
	for (ll i = 2; i <= n; i++) {
		if (!vis[i]) {
			prime[cnt++] = i;
		}
		for (int j = 0; j < cnt; j++) {
			if (i * (ll)prime[j] > 1e7)
				break;
			vis[i * prime[j]] = 1;
			if (i % prime[j] == 0)
				break;
		}
	}
}
ll num[maxn];
void init() {
	for (ll i = 3; i <= n; i++) {
		if (vis[i])
			num[i] = num[i - 1] + i;
		else num[i] = num[i - 1] + i * 2;
	}
}
int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	int T;
	cin >> T;
	ll arr[110] = { 0 };
	for (int i = 1; i <= T; i++) {
		cin >> arr[i];
		n = max(n, arr[i]);
	}
	Euler_prime();
	init();
	for (int i = 1; i <= T; i++) {
		cout << num[arr[i]] << "\n";
	}

	return 0;
}





6955.Xor Sum

思路:

1.A^ B=C —> A^C=B (异或运算的特点)
2.题目求一段异或和大于k的长度最小序列,则可以抽象为在异或前缀和中找两个数异或大于等于k且两者之间距离最小
3.可以使用01字典树来加速这一过程,复杂度为O(nlogn)

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 3e6 + 10;
const ll mod = 1e9 + 7;
int t, n, k, a;
int arr[maxn];
struct Trie {

	int ch[maxn][2];
	int sz;
	int val[maxn];//存放某数在数组中的坐标
	
	void inint() {
		memset(ch[0], 0, sizeof ch[0]);
		memset(val, 0, sizeof val);
		sz = 1;
	}

	void insert(int x,int step) {
		int root = 0;
		for (int i = 29; i >= 0; i--) {
			int v = (x >> i) & 1;

			if (!ch[root][v]) {
				memset(ch[sz], 0, sizeof ch[sz]);
				val[sz] = -1;
				ch[root][v] = sz++;
			}
			root = ch[root][v];
			val[root] = max(val[root], step);//记录root出现的最靠右的那个点的坐标
		}
	}

	int  search(int x) {
		int root = 0, res = -1;
		for (int i = 29; i >= 0; i--) {
			int v = (x >> i) & 1;
			if (!((k >> i) & 1)) {
				if (ch[root][v ^ 1]) {//贪心
					//如果v^1存在,那么k在第i为0,v^(v^1)==1,那么肯定是大于k的,更新答案
					res = max(res, val[ch[root][v ^ 1]]);
				}
				//如果k在第i位为0,则v^v==0
				root = ch[root][v];

			}
			else {//如果k在第i位为1,则贪心找(v^1)^(v)==1 
				root = ch[root][v ^ 1];
			}
			if (!root)
				break;
		}
		if (root)
			res = max(res, val[root]);

		return res;

	}
}tree;


int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin >> t;
	while (t--) {
		tree.inint();
		pair<int, int> ans;
		ans.first = ans.second = -1;
		int len = INT_MAX;
		cin >> n >> k;
		for (int i = 1; i <= n; i++) {
			//没插入一个数进行一次搜索,即把arr[i]作为答案右边界,看左边是否有满足情况的arr[j]
			cin >> arr[i];
			arr[i] = arr[i - 1] ^ arr[i];
			int  temp = tree.search(arr[i]);
			if (abs(temp - i) < len && temp >= 0) {
				len = abs(temp - i);
				ans.first = min(temp, i) + 1;
				ans.second = max(temp, i);
			}
			
			tree.insert(arr[i], i);

		}
		
		if (ans.first == -1) {
			cout << -1 << "\n";
		}
		else cout << ans.first << " " << ans.second << "\n";
	}


	return 0;
}





牛客暑假多校训练营1


A.Alice and Bob

蒟蒻先去学了学SG函数,发现一个神仙博主,推荐

1.本质一个暴力筛出所有结果的题目
2.只需要了解博弈图,就会发现怎么去筛结果
3.可以参考榜单第一的代码,没太看懂为啥那样,但是他的代码时间只有300+ms,蒟蒻的800+(还是菜了

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 5e3 + 10;
const ll mod = 1e9 + 7;

bool vis[maxn][maxn];

void init() {

	for (int i = 0; i <= 5000; i++) {//暴力每一个状态(图的每一个点
		for (int j = 0; j <= 5000; j++) {
			if (!vis[i][j]) {//如果一个点是必败状态,那么从此点出发,所有合法状态能走到的点都是必胜状态
				for (int k = 1; i + k <= 5000; k++) {//遍历所有合法操作
					for (int s = 0; s * k + j <= 5000; s++) {
						vis[i + k][j + s * k] = 1;
					}
				}
				for (int k = 1; j + k <= 5000; k++) {
					for (int s = 0; s * k + i <= 5000; s++) {
						vis[i + k * s][j + k] = 1;
					}
				}
			}
		}
	}

}

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif

	init();
	int t;
	cin >> t;
	int n, m;
	while (t--) {
		cin >> n >> m;
		if (vis[n][m])
			cout << "Alice" << "\n";
		else cout << "Bob\n";
		//puts(vis[n][m] ? "Alice" : "Bob");  使用puts又比cout快200+
	
	}


	return 0;
}




B.Ball Dropping

简单的初中平面几何。。。。当时卡死了,忘了初中的时候一条定理:不会的时候划辅助线找感觉

推导过程

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 5e3 + 10;
const ll mod = 1e9 + 7;

double r, a, b, h;

int main() {
//#ifndef ONLINE_JUDGE
//	freopen("1.in", "r", stdin);
//	freopen("1.out", "w", stdout);
//#endif

	cin >> r >> a >> b >> h;
	if (2 * r < b) {
		puts("Drop");
	}
	else {
		double AB = h * b / (2.0*(a/2-b/2));
		double AD = sqrt((AB + h) * (AB + h) + a * a / 4.0);
		double sinA = a / (2.0 * AD);
		double OA = r / sinA;
		double OB = OA - AB;
		printf("Stuck\n%.10f\n", OB);
	}
	return 0;
}





F.Find 3-friendly integers

1、队友在第一时间发现了结论:三位及以上位数的数,都是符合题意的3-friendly
2、然后把1-99的答案前缀和存一下,判一下LR的区间就可以了

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 110;
const ll mod = 1e9 + 7;

ll T, L, R;
int ans[maxn];
void init() {

	for (int i = 1; i <= 99; i++) {
		int tmep = i;
		if (tmep % 3 == 0)
			ans[i] = ans[i - 1] + 1;
		else {
			bool sy = 0;
			while (tmep) {
				if (tmep%10 % 3 == 0) {
					sy = 1;
				}
				tmep /= 10;
			}
			if (sy) {
				ans[i] = ans[i - 1] + 1;
			}
			else ans[i] = ans[i - 1];
		}
	}

}


int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	init();
	cin >> T;
	while (T--) {

		cin >> L >> R;
		if (L >= 100) {
			cout << R - L + 1 << "\n";
		}
		else if (L < 100) {
			if (R < 100) {
				cout << ans[R] - ans[L - 1]<<"\n";
			}
			else {
				cout << ans[99] - ans[L - 1] + R - 99 << "\n";
			}
		}

	}

	return 0;
}





G.Game of Swapping Numbers

1.朴素题意是k次 s w a p ( a i , a j ) swap(a_i,a_j) swap(ai,aj),让 ∑ i = 1 N ∣ a i − b i ∣ \sum_{i=1}^N |a_i-b_i| i=1Naibi尽可能地大。
2.我们可以先将a数组存放 m a x ( a i , b i ) max(a_i,b_i) max(ai,bi) ,b数组存放 m i n ( a i , b i ) min(a_i,b_i) min(ai,bi),此处的可行性稍作说明 壹 ^壹
3.当发生操作 s w a p ( a i , a j ) swap(a_i,a_j) swap(ai,aj)时,原本的 ∣ a i − b i ∣ + ∣ a j − b j ∣ |a_i-b_i|+|a_j-b_j| aibi+ajbj就会变成 ∣ a j − b i ∣ + ∣ a i − b j ∣ |a_j-b_i|+|a_i-b_j| ajbi+aibj,相当于说,一次操作之后,对原答案(没有操作)的影响就是 ( ∣ a j − b i ∣ + ∣ a i − b j ∣ ) − ( ∣ a i − b i ∣ + ∣ a j − b j ∣ ) (|a_j-b_i|+|a_i-b_j|)-(|a_i-b_i|+|a_j-b_j|) (ajbi+aibj)(aibi+ajbj)
因为a数组存放 m a x ( a i , b i ) max(a_i,b_i) max(ai,bi) ,b数组存放 m i n ( a i , b i ) min(a_i,b_i) min(ai,bi),且绝对值的存在,我们需要分类讨论。
前置说明: j > i j>i j>i
a j > b i , a i > b j : a_j>b_i,a_i>b_j: aj>bi,ai>bj:
( ∣ a j − b i ∣ + ∣ a i − b j ∣ ) − ( ∣ a i − b i ∣ + ∣ a j − b j ∣ ) = = ( a j − b i + a i − b j ) − ( a i − b i + a j − b j ) = = 0 (|a_j-b_i|+|a_i-b_j|)-(|a_i-b_i|+|a_j-b_j|)==(a_j-b_i+a_i-b_j)-(a_i-b_i+a_j-b_j)==0 (ajbi+aibj)(aibi+ajbj)==(ajbi+aibj)(aibi+ajbj)==0
所以这种情况的变换对答案是无影响的。
a j > b i , a i < b j : a_j>b_i,a_i<b_j: aj>bi,ai<bj: ( ∵ a j > b j ∴ a j > a i \because a_j>b_j\therefore a_j>a_i aj>bjaj>ai,同理 b j > a i b_j>a_i bj>ai)
( ∣ a j − b i ∣ + ∣ a i − b j ∣ ) − ( ∣ a i − b i ∣ + ∣ a j − b j ∣ ) = = ( a j − b i + b j − a i ) − ( a i − b i + a j − b j ) = = 2 ∗ b j − 2 ∗ a i (|a_j-b_i|+|a_i-b_j|)-(|a_i-b_i|+|a_j-b_j|)==(a_j-b_i+b_j-a_i)-(a_i-b_i+a_j-b_j)==2*b_j-2*a_i (ajbi+aibj)(aibi+ajbj)==(ajbi+bjai)(aibi+ajbj)==2bj2ai
所以这种情况的变换对答案的影响为 2 ∗ b j − 2 ∗ a i 2*b_j-2*a_i 2bj2ai
a j < b i , a i > b j : a_j<b_i,a_i>b_j: aj<biai>bj:( ∵ a i > b i ∴ a i > a j \because a_i>b_i \therefore a_i>a_j ai>biai>aj,同理 b i > b j b_i>b_j bi>bj)
( ∣ a j − b i ∣ + ∣ a i − b j ∣ ) − ( ∣ a i − b i ∣ + ∣ a j − b j ∣ ) = = ( b i − a j + a i − b j ) − ( a i − b i + a j − b j ) = = 2 ∗ b i − 2 ∗ a j (|a_j-b_i|+|a_i-b_j|)-(|a_i-b_i|+|a_j-b_j|)==(b_i-a_j+a_i-b_j)-(a_i-b_i+a_j-b_j)==2*b_i-2*a_j (ajbi+aibj)(aibi+ajbj)==(biaj+aibj)(aibi+ajbj)==2bi2aj
所以这种情况的变换对结果的影响为 2 ∗ b i − 2 ∗ a j 2*b_i-2*a_j 2bi2aj
a j < b i , a i < b j : a_j<b_i,a_i<b_j: aj<biai<bj: (不可能存在
!!!
综上讨论,我们可以得到这样一个结论:
I n f l u e n c e = m a x ( b i , b j ) − m i n ( a i , a j ) Influence=max(b_i,b_j)-min(a_i,a_j) Influence=max(bi,bj)min(ai,aj)
4.根据抽屉原理,N>2的时候,必存在一种①情况,所以当没有正影响因子存在时,剩余的k-x操作可以通过①操作来消耗掉,以满足恰好k次操作的要求

说明:
壹:
为什么可以将a存放max(a,b),b存放min(a,b)?
我们举个例子,
a={1,3}
b={1,4}
①朴素做法:
a-->{3,1}
ans==abs(3-1)+abs(1-4)==5
②优化做法:
a-->{1,4},b-->{1,3}
a-->{4,1},b-->{1,3}
ans=abs(4-1)+abs(1-3)==5

所以说,swap(a,b)对结果是无影响的
#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 5e5 + 10;
const ll mod = 1e9 + 7;

ll n, k;
ll a[maxn], b[maxn];
ll ans;

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif

	cin >> n >> k;
	
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}

	for (int i = 1; i <= n; i++) {
		cin >> b[i];
		
		if (b[i] > a[i])
			swap(b[i], a[i]);

		ans += a[i] - b[i];//无操作时的答案
	}

	sort(a + 1, a + 1 + n, less<>());//升序排列
	sort(b + 1, b + 1 + n, greater<>());//逆序排列

	for (int i = 1; i <= n && i <= k; i++) {

		ll dis = 2 * (b[i] - a[i]);//影响因子为maxb-mina
		if (dis <= 0)//根据抽屉原理+贪心,dis开始出现<=0,则不可能再出现dis>0的情况,所以可以break
			break;
		else ans += dis;
	}
	cout << ans << "\n";

	return 0;
}



H.Hash Function

后期补充

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 5e5 + 10;
const ll mod = 1e9 + 7;
const ll Len = 1 << 20;
const ll maxr = Len + 100;
const double PI = acos(-1.0);

struct Complex {
	double x, y;
	Complex(double _x = 0.0, double _y = 0.0) {
		x = _x;
		y = _y;
	}
	Complex operator-(const Complex& b)const {
		return Complex(x - b.x, y - b.y);
	}
	Complex operator+(const Complex& b)const {
		return Complex(x + b.x, y + b.y);
	}
	Complex operator*(const Complex& b)const {
		return Complex(x * b.x - y * b.y, x * b.y + y * b.x);
	}
};

void change(Complex y[], int len) {
	int k;
	for (int i = 1, j = len / 2; i < len - 1; i++) {
		if (i < j)
			swap(y[i], y[j]);
		k = len / 2;
		while (j >= k) {
			j = j - k;
			k = k / 2;
		}
		if (j < k)
			j += k;
	}
}
void fft(Complex y[], int len, int on) {
	change(y, len);
	for (int h = 2; h <= len; h <<= 1) {
		Complex wn(cos(2 * PI / h), sin(on * 2 * PI / h));
		for (int j = 0; j < len; j += h) {
			Complex w(1, 0);
			for (int k = j; k < j + h / 2; k++) {
				Complex u = y[k];
				Complex t = w * y[k + h / 2];
				y[k] = u + t;
				y[k + h / 2] = u - t;
				w = w * wn;

			}
		}
	}
	if (on == -1) {
		for (int i = 0; i < len; i++) {
			y[i].x /= len;
		}
	}
}
Complex a[maxr], b[maxr], c[maxr];
bool can[maxr];
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int n;
	cin >> n;
	for (int i = 0; i < n; i++) {
		int x;
		cin >> x;
		a[x].x = 1;
		b[maxn - x].x = 1;
	}
	fft(a, Len, 1);
	fft(b, Len, 1);
	for (int i = 0; i < Len; i++) {
		c[i] = a[i] * b[i];
	}
	fft(c, Len, -1);
	for (int i = 0; i <= Len; i++) {
		int x = (int)floor(c[i].x + 0.5);
		if (x > 0) {
			can[abs(i - maxn)] = 1;
		}
	}
	for (int i = n; i <= maxn; i++) {
		bool isok = true;
		for (int j = i; j <= maxn; j += i) {
			if (can[j]) {
				isok = false;
				break;
			}
		}
		if (isok) {
			cout << i << "\n";
			break;
		}
	}
	return 0;

}




K.Knowledge Test about Match

1.题意:给n个值,值域是[0,n-1],让你给它重新排序,以至于T的数据的函数平均值 < = 0.04 <=0.04 <=0.04,函数为 ( f y o u r − f s t d ) / f s t d (f^{your}-f^{std})/f^{std} (fyourfstd)/fstd,其中 f y o u r f^{your} fyour表示经过你排序后的序列 ∑ i = 0 n − 1 ∣ i − a i ∣ \sum_{i=0}^{n-1} \sqrt{|i-a_i|} i=0n1iai ,同理, f s t d f^{std} fstd表示函数值最小的情况。
2.思路:按照dis从小到大,如果出现符合条件的,暴力匹配它。

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 2e3 + 10;
const ll mod = 1e9 + 7;

int a[maxn],ans[maxn];//存题目给的序列、ans[i]:数字i匹配的数字的下标
bool vis[maxn];//标记一个下标是否被使用过
//存差值为x的组合都有那些
vector<pair<int,int> > cnt[maxn];

signed main()
{
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	ll T; cin >> T;
	while (T--) {
		
		ll n;
		cin >> n;
		//输入and初始化
		for (int i = 0; i < n; i++) {
			cin >> a[i];
			cnt[i].clear();
			ans[i] = -1;
			vis[i] = 0;
		}
		//暴力存储每一种差值的所有可能
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				cnt[abs(i - a[j])].push_back({ i,j });
			}
		}
		//枚举每种dis,保存答案,贪心
		for (int i = 0; i < n; i++) {
			for (auto [x, y] : cnt[i]) {
				if (ans[x] == -1 && !vis[y]) {
					ans[x] = y;
					vis[y] = 1;
				}
			}
		}

		for (int i = 0; i < n; i++) {
			cout << a[ans[i]] << " ";
		}
		cout << "\n";
	}


	return 0;

}



还有一种玄学做法。。。我看不懂,但我大为震撼

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e5 + 10;
const ll mod = 1e9 + 7;

ll T, n;
double a[maxn + 10];
ll b[maxn + 10];

signed main()
{
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	for (int i = 1; i <= maxn; i++)
		a[i] = sqrt(i);
	
	cin >> T;
	while (T--) {
		cin >> n;
		for (int i = 0; i < n; i++)
			cin >> b[i];
		sort(b , b + n);
		for (int t = 0; t < 2; t++) {//这里比较玄学,一次暴力过不了,两次就可以了,想不明白,但我大为震撼
			for (int i = 0; i < n; i++) {//暴力
				for (int j = i + 1; j < n; j++) {
					if (a[abs(b[i] - j)] + a[abs(b[j] - i)] < a[abs(b[i] - i)] + a[abs(b[j] - j)])
						swap(b[i], b[j]);
				}
			}
		}

		for (int i = 0; i < n; i++) {
			cout << b[i] << " ";
		}
		cout << "\n";
	}


	return 0;

}




牛客暑假多校训练营2


D.Er Ba Game

这题很签到,没啥说的(主要是补一下没做过的题

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e6 + 10;
const ll mod = 1e9 + 7;

signed main()
{
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);

	int T; cin >> T;
	while (T--) {
		int a1, a2, b1, b2;
		cin >> a1 >> b1 >> a2 >> b2;
		if (a1 > b1)
			swap(a1, b1);
		if (a2 > b2)
			swap(a2, b2);
		int ans = 0;
		if (a1 == 2 && b1 == 8 && (a2 != 2 || b2 != 8)) {
			ans = 1;
		}
		else if (a2 == 2 && b2 == 8 && (a1 != 2 || b1 != 8)) {
			ans = 2;
		}
		else if (a1 == b1) {
			if (a2 == b2) {
				if (a1 > a2)
					ans = 1;
				else if (a1 < a2)
					ans = 2;
			}
			else {
				ans = 1;
			}
		}
		else if (a2 == b2) {
			ans = 2;
		}
		else if ((a1 + b1) % 10 > (a2 + b2) % 10) {
			ans = 1;
		}
		else if ((a1 + b1) % 10 < (a2 + b2) % 10) {
			ans = 2;
		}
		else if (b1 > b2) {
			ans = 1;
		}
		else if (b1 < b2) {
			ans = 2;
		}
		if (ans == 1)
			cout << "first\n";
		else if (ans == 2)
			cout << "second\n";
		else cout << "tie\n";
	}

	return 0;

}




F.Girlfriend

题意:给你ABCD四个点的坐标 ( x , y , z ) (x,y,z) (x,y,z),其中满足 ∣ P 1 A ∣ ≥ k 1 ∗ ∣ P 1 B ∣ , ∣ P 2 C ∣ ≥ k 2 ∗ ∣ P 2 D ∣ |P_1A| \geq k_1*|P_1B|,|P_2C| \geq k_2*|P_2D| P1Ak1P1BP2Ck2P2D
思路:当 ∣ P 1 A ∣ = k 1 ∗ ∣ P 1 B ∣ |P_1A| = k_1*|P_1B| P1A=k1P1B时,满足阿波罗尼斯圆的定义,又因为是在空间中,所以 ∣ P 1 A ∣ ≥ k 1 ∗ ∣ P 1 B ∣ |P_1A| \geq k_1*|P_1B| P1Ak1P1B等价于空间中的球,其中 ∣ A B ∣ |AB| AB作为直径,那么接下来就是求出两个球的球心与半径,然后套用球交的板子

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e6 + 10;
const ll mod = 1e9 + 7;
const double PI = acos(-1);

struct pos {
	double x, y, z;
}p[5];

ll T;
double k1, k2;

signed main()
{
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin >> T;
	while (T--) {
		for (int i = 0; i < 4; i++) {
			cin >> p[i].x >> p[i].y >> p[i].z;
		}

		cin >> k1 >> k2;
		//这里的变量是把点带入上面的不等式,然后写出球的一般式,根据球心公式而得出的
		double temp1 = k1 * k1 - 1;
		double temp2 = k2 * k2 - 1;

		pos o1, o2;

		// 球1
		o1.x = (k1 * k1 * p[1].x - p[0].x) / temp1;
		o1.y = (k1 * k1 * p[1].y - p[0].y) / temp1;
		o1.z = (k1 * k1 * p[1].z - p[0].z) / temp1;

		double D1 = k1 * k1 * (p[1].x * p[1].x + p[1].y * p[1].y + p[1].z * p[1].z) - p[0].x * p[0].x - p[0].y * p[0].y - p[0].z * p[0].z;
		D1 /= temp1;

		double r1 = sqrt(o1.x * o1.x + o1.y * o1.y + o1.z * o1.z - D1);

		// 球2
		o2.x = (k2 * k2 * p[3].x - p[2].x) / temp2;
		o2.y = (k2 * k2 * p[3].y - p[2].y) / temp2;
		o2.z = (k2 * k2 * p[3].z - p[2].z) / temp2;

		double D2 = k2 * k2 * (p[3].x * p[3].x + p[3].y * p[3].y + p[3].z * p[3].z) - p[2].x * p[2].x - p[2].y * p[2].y - p[2].z * p[2].z;
		D2 /= temp2;

		double r2 = sqrt(o2.x * o2.x + o2.y * o2.y + o2.z * o2.z - D2);

		// 计算
		double dis = sqrt((o1.x - o2.x) * (o1.x - o2.x) + (o1.y - o2.y) * (o1.y - o2.y) + (o1.z - o2.z) * (o1.z - o2.z));

		// 不相交
		if (dis >= r1 + r2) {
			cout << fixed << setprecision(3) << 0 << "\n";
		}
		// 内含
		else if (dis <= abs(r1 - r2)) {
			cout << fixed<<setprecision(3)<<4.0 / 3.0 * PI * min(r1, r2) * min(r1, r2) * min(r1, r2) << "\n";
		}
		// 相交
		else {
		//模板
			double cos_r1 = (r1 * r1 + dis * dis - r2 * r2) / (2.0 * dis * r1);
			double h1 = r1 - r1 * cos_r1;
			double cos_r2 = (r2 * r2 + dis * dis - r1 * r1) / (2.0 * dis * r2);
			double h2 = r2 - r2 * cos_r2;
			cout << fixed << setprecision(3) << PI * h1 * h1 * (r1 - h1 / 3.0) + PI * h2 * h2 * (r2 - h2 / 3.0) << "\n";
		}

	}


	return 0;

}




I.Penguins

1.知识点:BFS+模拟
2.题意就是让你操作两只行为动作镜像的小企鹅,求到达各自终点时最优过程
3.一个操作如‘L’:右边可以往右走,左边不可以往左走,此时,本操作仍然有效,结果为右边可以往右走,左边不走

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e6 + 10;
const ll mod = 1e9 + 7;
string Map[2][22];

const int nex1[4][2] = { {1,0},{0,-1},{0,1},{-1,0} };//D L R U
const int nex2[4][2] = { {1,0},{0,1},{0,-1},{-1,0} };//D R L U
const char Way1[4] = { 'D','L','R','U' };
const char Way2[4] = { 'D','R','L','U' };
map<char, pair<int, int> > m= { {'D',make_pair(1, 0)}, {'L' , make_pair(0, -1)}, {'R' , make_pair(0, 1)}, {'U' , make_pair(-1, 0)} };


struct Emm {
	int x1, y1, x2, y2;//左右企鹅坐标
	string ans1, ans2;//其实只需要记录ans1,ans2需要时if判一下就行
	int step;//存操作步数,其实不需要,ans.size就是步数
}emm[10000];
Emm cnt;
bool vis[25][25][25][25];//标记一种状态是否走过
Emm bfs(int x1, int y1, int x2, int y2) {

	queue<Emm> Q;
	Q.push({ x1,y1,x2,y2,"","" ,0});
	vis[x1][y1][x2][y2] = true;
	while (!Q.empty()) {
		Emm tem = Q.front(); Q.pop();
		//到达终点
		if (tem.x1 == 0 && tem.y1 == 19 && tem.x2 == 0 && tem.y2 == 0)
			return tem;
		for (int i = 0; i < 4; i++) {
			int tx1 = tem.x1 + nex1[i][0];
			int ty1 = tem.y1 + nex1[i][1];
			int tx2 = tem.x2 + nex2[i][0];
			int ty2 = tem.y2 + nex2[i][1];
			//不可走时不走
			if (tx1 < 0 || ty1 < 0 || tx1>19 || ty1>19 || Map[0][tx1][ty1] == '#')
				tx1 = tem.x1, ty1 = tem.y1;
			if (tx2 < 0 || ty2 < 0 || tx2>19 || ty2>19 || Map[1][tx2][ty2] == '#')
				tx2 = tem.x2, ty2 = tem.y2;
			if (vis[tx1][ty1][tx2][ty2])
				continue;
			vis[tx1][ty1][tx2][ty2] = true;
			Q.push({ tx1,ty1,tx2,ty2,tem.ans1 + Way1[i],tem.ans2 + Way2[i] ,tem.step + 1 });
		}

	}


}
//输出路线图
void print() {
	int x = 19, y = 19;
	Map[0][x][y] = 'A';
	for (int i = 0; i < cnt.ans1.size(); i++) {
		x += m[cnt.ans1[i]].first;
		y += m[cnt.ans1[i]].second;
		//判断无效操作
		if (x < 0 || x>19 || y < 0 || y>19||Map[0][x][y]=='#') {
			x -= m[cnt.ans1[i]].first;
			y -= m[cnt.ans1[i]].second;
			continue;
		}
		Map[0][x][y] = 'A';
	}
	x = 19, y = 0;
	Map[1][x][y] = 'A';
	for (int i = 0; i < cnt.ans2.size(); i++) {
		x += m[cnt.ans2[i]].first;
		y += m[cnt.ans2[i]].second;
		//判断无效操作
		if (x < 0 || x>19 || y < 0 || y>19 || Map[1][x][y] == '#') {
			x -= m[cnt.ans2[i]].first;
			y -= m[cnt.ans2[i]].second;
			continue;
		}
		Map[1][x][y] = 'A';
	}
	for (int i = 0; i < 20; i++) {
		cout << Map[0][i] << " " << Map[1][i] << "\n";
	}
}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	for (int i = 0; i < 20; i++) {
		cin >> Map[0][i] >> Map[1][i];
	}

	cnt = bfs(19, 19, 19, 0);
	cout << cnt.step << "\n";
	//cout<<cnt.ans1.size()<<"\n; 也对
	cout << cnt.ans1;
	cout << "\n";
	print();

	return 0;

}



牛客暑假多校训练营3


B.Black and white

题意 n ∗ m n*m nm的图,每个点 ( i , j ) (i,j) (i,j)有一个贡献值 c [ i ] [ j ] c[i][j] c[i][j],让你把整个图涂黑, m i n ( ∑ c [ i ] [ j ] ) min(\sum c[i][j]) min(c[i][j])

特殊的是,如果在一个四格正方形中,三个格已经是黑色,那么第四个格子自动变为黑色, c [ i ] [ j ] = 0 c[i][j]=0 c[i][j]=0
思路
将行与列构建一个二部图,行 A i A_i Ai B j B_j Bj,通过连接 A i B j A_iB_j AiBj,形成一颗生成树,那么此时生成树的边与完全二部图的边形成的补集,可以通过魔法自动生成!
那么问题就转变成了求一颗最小生成树

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e5 + 10;
const ll mod = 1e9 + 7;
ll n, m, a, b, c, d, p, ans,cnt;
int fa[maxn];
vector<pair<int, int> > arr[maxn];

ll find(int x) {
	if (x != fa[x])
		fa[x] = find(fa[x]);
	return fa[x];
}

void Kruskal() {
	for (int i = 0; i < 10010; i++) {
		fa[i] = i;
	}
	for (int i = 0; i < maxn; i++) {
		for (auto k : arr[i]) {
			int tx = find(k.first);
			int ty = find(k.second);
			if (tx == ty)
				continue;
			fa[tx] = ty;
			ans += i;
			if (++cnt == n + m - 1) {
				cout << ans << "\n";
				return;
			}
		}
	}
}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);

	cin >> n >> m >> a >> b >> c >> d >> p;
	ll last = a;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			last = (last * last * b + last * c + d) % p;
			arr[last].push_back({ i,j+n });
		}
	}

	Kruskal();

	return 0;

}

C.Minimum grid

知识点:贪心+二分图匹配
题意:有一个 n ∗ n \pmb{n*n} nnnnnn的空白矩阵,给你 m \pmb{m} mmm个点的坐标,让你往这 m \pmb{m} mmm个空白点里填数字,数字范围是 0 ≤ a i j ≤ k \pmb{0 \leq a_{ij} \leq k} 0aijk0aijk0aijk,要满足一下条件:
1.每一行的最大值为   b [ i ] ~\pmb{b[i]}  b[i]b[i]b[i]
2.每一列的最大值为   c [ i ] ~\pmb{c[i]}  c[i]c[i]c[i]
3. ∑ i = 1 n ∑ j = 1 n a i j   \pmb{\sum_{i=1}^n \sum_{j=1}^n a_{ij}}~ i=1nj=1naiji=1nj=1naiji=1nj=1naij 要最小(没有数字的地方默认为0,因为题目要求是求m个点的和)

贪心:
如果一行里面有2个数 a 1 , a 2 \pmb{a_1,a_2} a1,a2a1,a2a1,a2,其中 a 1 = b [ i ] \pmb{a_1=b[i]} a1=b[i]a1=b[i]a1=b[i],并且 a 2 \pmb{a_2} a2a2a2所在列也有元素 = c [ j ] \pmb{=c[j]} =c[j]=c[j]=c[j],那么为了满足条件3 a 2 \pmb{a_2} a2a2a2的最优值是不是0
因此,如果一个数,既不充作   b [ i ]   ~\pmb{b[i]}~  b[i]b[i]b[i] ,也不充作   c [ j ]   ~\pmb{c[j]}~  c[j]c[j]c[j] ,那么这个点值最优为0.
因此最坏的情况就是一行贡献一个   b [ i ]   ~\pmb{b[i]}~  b[i]b[i]b[i] ,一列贡献一个   c [ i ]   ~\pmb{c[i]}~  c[i]c[i]c[i] ,即 a n s = ∑ i = 1 n b [ i ] + ∑ j = 1 n c [ j ] \pmb{ans=\sum_{i=1}^nb[i]+\sum_{j=1}^nc[j]} ans=i=1nb[i]+j=1nc[j]ans=i=1nb[i]+j=1nc[j]ans=i=1nb[i]+j=1nc[j]
继续思考,什么情况下,会比更坏情况更优呢?
比如 b [ 2 ] = c [ 2 ] = w b[2]=c[2]=w b[2]=c[2]=w,而 ( 2 , 2 ) (2,2) (2,2)属于 m \pmb{m} mmm个填充点之一,那么如果让   a 2 , 2 = w   ~\pmb{a_{2,2}=w}~  a2,2=wa2,2=wa2,2=w 一个点就满足了一行一列的要求,这就是更优的情况!
我们把   b [ i ] = b [ j ]   ~\pmb{b[i]=b[j]}~  b[i]=b[j]b[i]=b[j]b[i]=b[j] 的行列连边,接下来的任务就是寻找行列的最大匹配。

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 2e3 + 10;
const ll mod = 1e9 + 7;
ll n, m, k, u, v;
ll b[maxn], c[maxn], match[maxn];
bool vis[maxn];
vector<int> edge[maxn];
ll ans;

// 匈牙利算法 求最大二分图匹配
bool dfs(int u) {

	for (auto v : edge[u]) {
		if (vis[v])
			continue;
		vis[v] = true;
		if (!match[v] || dfs(match[v])) {
			match[v] = u;
			return true;
		}
	}
	return false;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif

	cin >> n >> m >> k;
	for (int i = 1; i <= n; i++) {
		cin >> b[i];
		ans += b[i];
	}
	for (int i = 1; i <= n; i++) {
		cin >> c[i];
		ans += c[i];
	}
	while (m--) {
		cin >> u >> v;
		if (b[u] == c[v]) {
			edge[u].emplace_back(v);
		}
	}
	for (int i = 1; i <= n; i++) {
		memset(vis, 0, sizeof vis);
		dfs(i);
	}
	for (int i = 1; i <= n; i++) {
		// 因为在匈牙利算法中,match的对象为列 match[v]=u 所以此处减去的是列值
		if (match[i])
			ans -= c[i];
	}
	cout << ans;
	return 0;
}





E.Math

题意:求 1 ≤ x ≤ y ≤ n \pmb{1\leq x \leq y\leq n} 1xyn1xyn1xyn且满足 ( x 2 + y 2 ) % ( x ∗ y + 1 ) = 0 \pmb{(x^2+y^2)\%(x*y+1)=0} (x2+y2)%(xy+1)=0(x2+y2)%(xy+1)=0(x2+y2)%(xy+1)=0的数对 ( x , y ) \pmb{(x,y)} (x,y)(x,y)(x,y)数量。

刚开始没什么思路,小范围打标看一下规律

#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e6 + 10;
const ll mod = 1e9 + 7;

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	int num = 1;
	for (ll x = 1; x <= 9000; x++) {
		for (ll y = x; y <= 9000; y++) {
			if ((x * x + y * y) % (x * y + 1) == 0) {
				cout << "x=" << x << "\ty=" << y << "\t|\t";
				if (num++ % 5 == 0)
					cout << '\n';
			}
		}
	}

	return 0;
}

结果:
打标结果

首先我们可以发现,对于任意的一个 ( x , x 3 ) (x,x^3) (x,x3) 都满足情况

1 − 1 3 − 1 1-1^3-1 1131
2 − 2 3 − 8 2-2^3-8 2238
3 − 3 3 − 27 3-3^3-27 33327
············

这个结论是我们通过小范围打表发现的,不能保证百分百正确,所以我们要验证一下。
验证代码:

#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e6 + 10;
const ll mod = 1e9 + 7;

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	bool sy = 0;
	for (ll x = 1; x <= 1e6; x++) {
		ll y = x * x * x;
			if (!(x * x + y * y) % (x * y + 1) ) {
				sy = 1;
			}
	}
	cout << sy << "\n";

	return 0;
}

我们通过这一段代码,找出了 1 − 1 e 6 1-1e6 11e6的所有 ( x , x 3 ) (x,x^3) (x,x3)组合,发现果然是满足的!
但是我们小范围打的表除了 ( x , x 3 ) (x,x^3) (x,x3),还有一些不和谐的数字,我们整理下:

8-30-112-418-1560-5822
27-240-2133
64-1020
125-3120

我们可以发现两个规律:
1.开头为 x 3 x^3 x3
2. a i = a i − 1 ∗ x 2 + a i − 2 a_i=a_{i-1}*x^2+a_{i-2} ai=ai1x2+ai2
所以,对于每一个数对   ( x , x 3 )   ~(x,x^3)~  (x,x3) 都可以递归生成一个可行序列   a   ~a~  a ,其中每一个 ( a i , a i + 1 ) (a_{i},a_{i+1}) (aiai+1)都是符合条件的数对

因为 x ≤ y x\leq y xy,所以我们只需要把所有的 a i + 1 a_{i+1} ai+1存入数组,再从小到大排序,进行二分查找,即可找到 y ≤ n y\leq n yn的所有满足条件的数对 ( x , y ) (x,y) (x,y)数量

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define int128 __int128
const ll maxn = 1e6 + 10;
const ll mod = 1e9 + 7;

ll T, n;
vector<ll> vis;


int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	vis.push_back(1);
	for (int128 i = 2; i <= 1e6; i++) {
		int128 x = i, y = i * i * i, k = i * i;//这里会爆long long
		while (y <= 1e18) {
			vis.push_back(y);
			int128 temp = k * y - x;
			x = y;
			y = temp;
		}
	}
	sort(vis.begin(), vis.end());
	cin >> T;
	while (T--) {
		cin >> n;
		ll ans = upper_bound(vis.begin(), vis.end(), n) - vis.begin();
		cout << ans << "\n";
	}
	

	return 0;
}




F.24dian

题意:给你n张牌,牌面数字是1~13里随机的,让你通过 + , − , ∗ , / , ( ) +,-,*,/,() +,,,/,(),与n个数字拼成一个表达式,表达式的结果是m,问共有多少种数字方案?
一种方案是合法的要满足表达式中的某一步运算结果必须为分数(存在小数)

n = 1 \pmb{n=1} n=1n=1n=1 n = 2 \pmb{n=2} n=2n=2n=2的时候,肯定是没有数列满足合法条件的,因为某一步运算结果必须为分数,而结果又是一个整数, a 1 % a 2 ! = 0 , a 1 % a 2 = 0 \pmb{a_1\%a_2!=0 , a_1\%a_2=0} a1%a2!=0,a1%a2=0a1%a2!=0,a1%a2=0a1%a2!=0,a1%a2=0是不能同时成立的。
n = 3 \pmb{n=3} n=3n=3n=3的时候,假设 a 1 % a 2 ! = 0 \pmb{a_1\%a_2!=0} a1%a2!=0a1%a2!=0a1%a2!=0,那么 a 1 / a 2 ∗ a 3 \pmb{a_1/a_2*a_3} a1/a2a3a1/a2a3a1/a2a3是符合条件的,那是不是 a 1 ∗ a 3 / a 2 \pmb{a_1*a_3/a_2} a1a3/a2a1a3/a2a1a3/a2满足结果等于m,但是运算过程中是没有分数存在的(同级运算从左到右)
综上所述,n<4的时候,答案全为0

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e6 + 10;
const ll mod = 1e9 + 7;
const double eps = 1e-8;
ll n, m;

bool tge;//标记该数列结果等于m的所有组合是否符合出现分数的条件
bool ok;//标记是否进入过tge的判断

vector<vector<double> > ans;

//判断是否存在小数
bool isok(double x) {
	if (x - (int)x >= eps)
		return true;
	return false;
}

// 爆搜出一组数字组合的所有符合组合
void dfs_symbol(vector<double> num,bool vis) {

	//每次操作和2为1,最后一个数就是表达式结果
	if (num.size() == 1) {
		if (abs(num[0]- m)<eps) {
			if(!vis)// 该数列的一种组合情况出现了没有出现分数的情况
				tge = 0;
			ok = 1;
		}
		return;
	}
	// 两个for循环+递归 模拟符号的摆放位置
	for (int i = 0; i < num.size(); i++) {
		for (int j = 0; j < num.size(); j++) {
			if (i == j) //一个数字只能用一次
				continue;
			vector<double> temp = num;
			double kn;
			
			// +
			kn = num[i] + num[j];
			num.erase(num.begin() + max(i, j));//先删除后面的数,以免序号混乱
			num.erase(num.begin() + min(i, j));
			num.emplace_back(kn);
			dfs_symbol(num,vis);

			// -
			num = temp;
			kn = num[i] - num[j];
			num.erase(num.begin() + max(i, j));
			num.erase(num.begin() + min(i, j));
			num.emplace_back(kn);
			dfs_symbol(num,vis);

			// *
			num = temp;
			kn = num[i] * num[j];
			num.erase(num.begin() + max(i, j));
			num.erase(num.begin() + min(i, j));
			num.emplace_back(kn);
			dfs_symbol(num,vis);

			// /
			num = temp;
			if (num[j] != 0) {//分母不能为0
				kn = num[i]/num[j];
				num.erase(num.begin() + max(i, j));
				num.erase(num.begin() + min(i, j));
				num.emplace_back(kn);
				dfs_symbol(num,vis|isok(kn));
			}

			num = temp;
		}
	}


}

// 爆搜出所有的数字组合,暂且先不管符号
void dfs_num(vector<double> num, int len,int last) {
	if (len == n) {
		tge = 1;
		ok = 0;
		dfs_symbol(num,0);
		if (ok&&tge)
			ans.emplace_back(num);
		return;
	}
	for (int i = last; i <= 13; i++) {
		num.emplace_back(i);
		dfs_num(num, len + 1, i);
		num.pop_back();
	}
	return;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	cin >> n >> m;
	if (n < 4) {
		cout << 0 << "\n";
		return 0;
	}
	dfs_num({}, 0,1);
	cout << ans.size() << "\n";
	for (auto k : ans) {
		for (auto kk : k) {
			cout << kk << " ";
		}
		cout << "\n";
	}
	return 0;
}





2021牛客暑期多校训练营4


C.LCS

水题,贪心一下就可以暴力了

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e6 + 10;
const ll mod = 1e9 + 7;

int a, b, c, n;

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif

	cin >> a >> b >> c >> n;
	if (a + b - n > c||a+c-n>b||b+c-n>a) {//a+b-n 表示在1-2,2-3的前提下,1-3最少要重复的数量
		cout << "NO";
		return 0;
	}
	int te = min(a, min(b, c));
	a -= te, b -= te, c -= te;
	string ansa = "", ansb = "", ansc = "";
	for (int i = 1; i <= te; i++) {
		ansa += 'a', ansb += 'a', ansc += 'a';
	}
	while (a--) {
		ansa += 'b';
		ansb += 'b';
	}
	while (b--) {
		ansb += 'c';
		ansc += 'c';
	}
	while (c--) {
		ansa += 'd';
		ansc += 'd';
	}
	while (ansa.size() < n) {
		ansa += 'r';
	}
	while (ansb.size() < n) {
		ansb += 's';
	}
	while (ansc.size() < n) {
		ansc += 't';
	}
	cout << ansa << "\n" << ansb << "\n" << ansc;
	return 0;
}





I.Inverse

思维
你可以给序列 a \pmb{a} aaa的每一个数 + 1   o r + 0   +1 ~or +0~ +1 or+0 ,让 a \pmb{a} aaa的逆序对最少。
首先,一个数不论+1与否,对后面原本就比他小的数不影响,所以我们只需要看这个数前面有没有值为 a i + 1 a_i+1 ai+1的数,如果存在,那么这次变化就是有效的,如果不存在,就没有必要+1了
所以在遍历的过程中维护一个unordered_set
维护完之后用树状数组求逆序对就可以了

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e6 + 10;
const ll mod = 1e9 + 7;
#define M 500010
using namespace std;
int a[M], d[M], t[M], n;
int lowbit(int x)
{
	return x & -x;
}
void add(int x)//把包含这个数的结点都更新 
{
	while (x <= n)//范围 
	{
		t[x]++;
		x += lowbit(x);
	}
}
int sum(int x)//查询1~X有几个数加进去了 
{
	int res = 0;
	while (x >= 1)
	{
		res += t[x];
		x -= lowbit(x);
	}
	return res;
}
bool cmp(int x, int y)//离散化比较函数 
{
	if (a[x] == a[y]) return x > y;//避免元素相同 
	return a[x] > a[y];//按照原序列第几大排列 
}

unordered_set<int> vis;

int main()//402002139
{
	//freopen("in.txt","r",stdin);
	long long ans = 0;
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i], d[i] = i;

	for (int i = 1; i <= n; i++) {
		if (vis.find(a[i] + 1) != vis.end()) {
			a[i] += 1;
		}
		else {
			vis.insert(a[i]);
		}
	}

	sort(d + 1, d + n + 1, cmp);//离散化 

	for (int i = 1; i <= n; i++)
	{
		add(d[i]);//把这个数放进去 
		ans += sum(d[i] - 1);//累加 
	}
	cout << ans;
	return 0;
}






2021牛客暑期多校训练营5


B.Boxes

题意:共有 n \pmb{n} nnn个盒子,每个盒子里面放置了一个黑球或者一个白球,出现黑球与白球的概率相等,都是   1 2   ~\pmb{\frac{1}{2}}~  212121 ,每打开一个盒子都要付出   w i   ~\pmb{w_i}~  wiwiwi 的代价,让你求知道所有盒子里的颜色所需花费的最小期望。你可以通过花费 C \pmb{C} CCC来购买情报:盒子中黑球一共有多少个

1.首先我们讨论要不要买情报,什么时候买情报?

  • 要不要买情报需要比较买情报后的开销与直接全开的开销大小关系
  • 最买情报的最佳时间是开第一个箱子前,因为你在买情报之前根本不知道会有多少个黑色球,所以晚买很容易造成多开了几个箱子。

2.如果购买了情报,应该怎么开箱子呢

  • 有了情报信息,我们就可以从开箱代价最小的开始,因为期望要最小
  • 假设有 x \pmb{x} xxx个黑色球,我们开到第 i \pmb{i} iii个箱子,刚好开出了全部的黑色球,那么这种情况的概率是多少呢?

    因为黑色球已经全部开了出来,那么剩下的 n − i \pmb{n-i} ninini的球全是白色的了
    因为第i个箱子刚好开了出来,所以 C o l o r i , C o l o r i + 1 \pmb{Color_i,Color_{i+1}} Colori,Colori+1Colori,Colori+1Colori,Colori+1颜色不同

    所以我们可以通过这两条信息来求该状态下的期望。
    a n s = ∑ i = 0 n − 1 v a l u e [ i ] ∗ p [ i ] ans=\sum_{i=0}^{n-1}value[i]*p[i] ans=i=0n1value[i]p[i]
    v a l u e [ i ] : \pmb{value[i]:} value[i]:value[i]:value[i]:打开第 i \pmb{i} iii个箱子时的总开销
    p [ i ] : \pmb{p[i]:} p[i]:p[i]:p[i]:打开第 i \pmb{i} iii个箱子刚好把黑色球全开出来的概率
    根据条件一,后面 n − i \pmb{n-i} ninini个箱子颜色相同的概率时 ( 1 2 ) n − i (\pmb{\frac{1}{2})^{n-i}} (21)ni21)ni21)ni
    根据条件二,第 i \pmb{i} iii个箱子与第 i + 1 \pmb{i+1} i+1i+1i+1个箱子颜色不同的概率是 1 2 \pmb{\frac{1}{2}} 212121
    目前我们讨论的是黑色球,白色球是不是还有一种情况
    所以:
    p [ i ] = ( 1 2 ) n − i ∗ 1 2 ∗ 2 = ( 1 2 ) n − i p[i]=(\frac{1}{2})^{n-i}*\frac{1}{2}*2=(\frac{1}{2})^{n-i} p[i]=(21)ni212=(21)ni
    开销肯定就是前面开箱子的所有消费:
    v a l u e [ i ] = v a l u e [ i − 1 ] + w [ i ] value[i]=value[i-1]+w[i] value[i]=value[i1]+w[i]

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e5 + 10;
const ll mod = 1e9 + 7;

ll n;
double c;
double w[maxn];
// 快速幂
double qpow(double x, int y) {
	double ans = 1.0;
	while (y) {
		if (y & 1)
			ans *= x;
		x *= x;
		y >>= 1;
	}
	return ans;
}
int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	cin >> n >> c;
	double ans = 0, last =0, W = 0;
	for (int i = 1; i <= n; i++) {
		cin >> w[i];
		W += w[i];
	}
	sort(w + 1, w + 1 + n);
	for (int i = 0; i < n; i++) {
		last += w[i];
		ans += last * qpow(0.5, n - i );
	}
	ans = min(ans + c, W);//如果直接开要比买情报的最低期望还要低,那就直接开
	printf("%.7f", ans);
	return 0;
}

D.Double Strings

题意:给你两个字符串 a , b \pmb{a,b} a,ba,ba,b,让你在 a , b \pmb{a,b} a,ba,ba,b中挑出等量的字符,按顺序组成 A , B \pmb{A,B} A,BA,BA,B,使得 A , B \pmb{A,B } A,BA,BA,B是good的。
good定义:存在下标 i \pmb{i} iii,使得 A j = B j , j ∈ [ 1 , i − 1 ] , A j < B j \pmb{A_j=B_j,j \in [1,i-1],A_j<B_j} Aj=Bj,j[1,i1],Aj<BjAj=Bj,j[1,i1],Aj<BjAj=Bj,j[1,i1],Aj<Bj
问有多少种字串符合条件

我们可以用dp求出公共子串的数量,每一种公共子串的后面可以任意添加等量字符。
比如 d p [ i ] [ j ] \pmb{dp[i][j]} dp[i][j]dp[i][j]dp[i][j]表示在 a \pmb{a} aaa的前 i \pmb{i} iii个里和 b \pmb{b} bbb的前 j \pmb{j} jjj里能选出的公共子串的种类数,那么 a \pmb{a} aaa的后 n \pmb{n} nnn里, b \pmb{b} bbb的后 m \pmb{m} mmm个里,我们选择等量的字符作为后缀:
C n 0 ∗ C m 0 + C n 1 ∗ C m 1 + ⋅ ⋅ ⋅ + C n m i n ( n , m ) ∗ C m m i n ( n , m ) C_n^0*C_m^0+C_n^1*C_m^1+···+C_n^{min(n,m)}*C_m^{min(n,m)} Cn0Cm0+Cn1Cm1++Cnmin(n,m)Cmmin(n,m)
上面的公式可以化简为:
C n + m m i n ( n , m ) C_{n+m}^{min(n,m)} Cn+mmin(n,m)
推导过程:
C n 0 ∗ C m 0 + C n 1 ∗ C m 1 + ⋅ ⋅ ⋅ + C n m ∗ C m m ( m ≤ n ) − − − ( 1 ) C_n^0*C_m^0+C_n^1*C_m^1+···+C_n^{m}*C_m^{m}(m\leq n)---(1) Cn0Cm0+Cn1Cm1++CnmCmm(mn)(1)

等价于

C n 0 ∗ C m m + C n 1 ∗ C m m − 1 + ⋅ ⋅ ⋅ + C n m ∗ C m 0 ( m ≤ n ) − − − ( 2 ) C_n^0*C_m^m+C_n^1*C_m^{m-1}+···+C_n^{m}*C_m^0(m\leq n)---(2) Cn0Cmm+Cn1Cmm1++CnmCm0(mn)(2)
式(2)表示从n,m两堆中选出m个物品都多少种可能,相当于在n+m的大池子里选择m个物品的种类

又因为组合数比较大,需要预处理阶乘逆元

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e6 + 10;
const ll mod = 1e9 + 7;

string a, b;
ll dp[5010][5010];
ll fact[10010];
ll inv[10010];
void init() {
	fact[0] = 1;
	for (ll i = 1; i <= 10002; i++) {
		fact[i] = (fact[i - 1] * i) % mod;
	}
	inv[0] = inv[1] = 1;
	// 处理之后, inv[i]代表i的逆元
	for (ll i = 2; i <= 10005; i++) {
		inv[i] = (mod - mod / i) * inv[mod % i] % mod;
	}
	// 处理之后,inv[i]代表i!的逆元
	for (ll i = 2; i <= 10005; i++) {
		inv[i] = (inv[i] * inv[i - 1]) % mod;
	}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	cin >> a >> b;
	a = '0' + a;
	b = '0' + b;
	init();
	// 计算公共子序列的个数
	for (int i = 1; i < a.size(); i++) {
		for (int j = 1; j < b.size(); j++) {
			dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1];//可能出现负
			if (a[i] == b[j])
				dp[i][j] += dp[i - 1][j - 1] + 1;
			dp[i][j] = (dp[i][j] + mod) % mod;
		}
	}
	ll ans = 0;
	for (int i = 1; i < a.size(); i++) {
		for (int j = 1; j < b.size(); j++) {
			if (a[i] < b[j]) {
				ll n = a.size() - i - 1;
				ll m = b.size() - j - 1;
				if (n < m)
					swap(n, m);
				// dp+1 : 1代表两者没有公共子序列
				ans = (ans + (dp[i - 1][j - 1] + 1) * fact[n + m] % mod * inv[n] % mod * inv[m] % mod) % mod;
			}
		}
	}
	cout << ans;
	return 0;
}





H.Holding Two

题意:让你构造一个图,其中任意一个点的 a i , j , a i + 1 , j , a i + 2 , j    o r    a i , j , a i , j + 1 , a i , j + 2    o r    a i , j , a i + 1 , j + 1 , a i + 2 , j + 2 a_{i,j},a_{i+1,j},a_{i+2,j} ~~or ~~a_{i,j},a_{i,j+1},a_{i,j+2} ~~or~~a_{i,j},a_{i+1,j+1},a_{i+2,j+2} ai,j,ai+1,j,ai+2,j  or  ai,j,ai,j+1,ai,j+2  or  ai,j,ai+1,j+1,ai+2,j+2 都不能出现相等
因为题目是连续三个位置,那么我们可以构造一个以2为循环的图
简单题:当时队友做的,补一下

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e6 + 10;
const ll mod = 1e9 + 7;

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif

	int n, m;
	cin >> n >> m;
	int sy = 0, sum = 0;
	for (int i = 1; i <= n; i++) {
		if ((sum %= 2) == 0)
			sy = !sy;
		for (int j = 1; j <= m; j++) {
			if (sy) {
				if (j % 2)
					cout << 0 ;
				else 
					cout << 1 ;
			}
			else {
				if (j % 2)
					cout << 1 ;
				else 
					cout << 0 ;
			}
		}
		sum++;
		cout << "\n";
	}

	return 0;
}

J.Jewels

题意:刚开始在海里有 n n n个的珍珠,每个珍珠有一个坐标 ( x , y , z ) (x,y,z) (x,y,z),并且他还有一个上升速度   v   ~v~  v ,你每秒钟可以收集一个珍珠,代价是 v a l u e = x ∗ x + y ∗ y + ( z + v ∗ t ) ∗ ( z + v ∗ t ) value=x*x+y*y+(z+v*t)*(z+v*t) value=xx+yy+(z+vt)(z+vt),让你求出拿回所有珍珠的总代价最小是多少

每秒钟都要从 n n n个珍珠里面选一个,而且选过的珍珠还不能再次选择,我们可以用带权二分图匹配来解决这个问题,我们可以套用KM来求得答案。

友情链接:
KM前置知识:匈牙利算法
KM

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 350;
const ll mod = 1e9 + 7;
const ll inf = 0x3f3f3f3f3f3f3f3f;

ll n, vx[maxn], vy[maxn], xtoy[maxn], ytox[maxn];
ll visx[maxn], visy[maxn], pre[maxn], sy[maxn], a[maxn][maxn];
ll z[maxn], v[maxn];
void aug(int y) {
	for (int nxt, x; y; y = nxt) {
		nxt = xtoy[x = pre[y]];
		ytox[y] = x;
		xtoy[x] = y;
	}
}
void jzm(int x) {
	queue<int>q;
	memset(visx, 0, sizeof visx);// 通过是不是走过,以及每次清空,来判断是不是在S中。
	memset(visy, 0, sizeof visy);
	for (int i = 1; i <= n; i++)
		sy[i] = inf;
	q.push(x);
	while (1) {// 直到找到了x的匹配
		while (!q.empty()) {
			int u = q.front(); q.pop();
			visx[u] = 1;
			for (int i = 1; i <= n; i++) {// 枚举y部
				if (visy[i] )// i是不是走过|| u-i有没有路,本体是有的,所以不需要判
					continue;
				ll d = vx[u] + vy[i] - a[u][i];
				if (d == 0) {// 相等边
					visy[i] = 1;
					pre[i] = u;
					if (!ytox[i]) {// 空点
						aug(i);
						return;// 找到匹配
					}
					else
						q.push(ytox[i]);// 从它的原配开始继续找增广路
				}
				else if (sy[i] > d)// 不是相等边,就可以更新d了
					sy[i] = d, pre[i] = u;
			}
		}
		ll del = inf;
		for (int i = 1; i <= n; i++)
			if (!visy[i])
				del = min(del, sy[i]);
		for (int i = 1; i <= n; i++) {
			if (visx[i])
				vx[i] -= del;
			if (visy[i])
				vy[i] += del;
			else if (sy[i] != inf)
				sy[i] -= del;
		}
		for (int i = 1; i <= n; i++) {
			if (!visy[i] && !sy[i]) {
				visy[i] = 1;
				if (!ytox[i]) {
					aug(i);
					return;
				}
				else
					q.push(ytox[i]);
			}
		}
	}
}

ll KM() {
	ll ans = 0;
	// 初始化顶标
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			vx[i] = max(vx[i],a[i][j]);
		}
	}
	
	for (int i = 1; i <= n; i++) {
		jzm(i);
	}
	for (int i = 1; i <= n; i++) {
		ans += vx[i] + vy[i];
	}

}

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	cin >> n;
	ll x, y, ans = 0;
	
	for (int i = 1; i <= n; i++) {
		cin >> x >> y >> z[i] >> v[i];
		// 因为x,y随时间对答案无影响,可以先记录下来
		ans += x * x + y * y;
	}
	
	for (int i = 1; i <= n; i++) {// 时间
		for (int j = 1; j <= n; j++) {// 点
			// 第几次拿第几个点
			a[i][j] = -(z[j] + v[j] * (i - 1)) * (z[j] + v[j] * (i - 1));
			// 因为KM求最大权完美匹配,把权值取符号,就变成了最小权
		}
	}
	ans -= KM();
	cout << ans;
	return 0;
}





K.King of Range

题意:给你一个长度为 n \pmb{n} nnn的序列,然后给你 m \pmb{m} mmm次询问,每次询问一个整数 k \pmb{k} kkk,问你存在多少的区间 [ l , r ] \pmb{[l,r]} [l,r][l,r][l,r],使得 m a x ( a i ∈ [ l , r ] ) − m i n ( a i ∈ [ l , r ] ) > k \pmb{max(a_{i\in[l,r]})-min(a_{i\in[l,r]})>k} max(ai[l,r])min(ai[l,r])>kmax(ai[l,r])min(ai[l,r])>kmax(ai[l,r])min(ai[l,r])>k

首先我们可以利用ST表,在 O ( n l o g n ) \pmb{O(nlogn)} O(nlogn)O(nlogn)O(nlogn)的时间里预处理出所有的区间最大值and最小值,并且 O ( 1 ) O(1) O(1)查询。
如果 [ l , r ] \pmb{[l,r]} [l,r][l,r][l,r]符合条件,那么 [ l , r + 1 ] , [ l , r + 2 ] , [ l , r + 3 ] a n d    s o   o n \pmb{[l,r+1],[l,r+2],[l,r+3] and ~~so ~on} [l,r+1],[l,r+2],[l,r+3]and  so on[l,r+1],[l,r+2],[l,r+3]and  so on[l,r+1],[l,r+2],[l,r+3]and  so on都符合条件,所以我们枚举 l \pmb{l} lll,找到最小的一个 r \pmb{r} rrr,使得 [ l , r ] \pmb{[l,r]} [l,r][l,r][l,r]满足条件, a n s + = n − r + 1 \pmb{ans+=n-r+1} ans+=nr+1ans+=nr+1ans+=nr+1

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e5 + 10;
const ll mod = 1e9 + 7;

int n, m, k;
int stmax[maxn][22];//第二维不得小于log(maxn)
int stmin[maxn][22];
int Log[maxn];// 优化查询
int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	cin >> n >> m;
	// 创建st表
	// **********************************
	for (int i = 1; i <= n; i++) {
		cin >> stmax[i][0];
		stmin[i][0] = stmax[i][0];
	}
	for (int i = 1; i <= 21; i++) {
		for (int j = 1; j + (1 << i) - 1 <= n; j++) {
			stmax[j][i] = max(stmax[j][i - 1], stmax[j + (1 << (i - 1))][i - 1]);
			stmin[j][i] = min(stmin[j][i - 1], stmin[j + (1 << (i - 1))][i - 1]);
		}
	}
	for (int i = 2; i <= n; i++) {
		Log[i] = Log[i / 2] + 1;
	}
	// **********************************
	while (m--) {
		cin >> k;
		ll ans = 0;
		int j = 1;
		for (int i = 1; i <= n; i++) {
			while (j <= n && j >= i) {
				int s = Log[j - i + 1];
				int ma = max(stmax[i][s], stmax[j - (1 << s) + 1][s]);
				int mi = min(stmin[i][s], stmin[j - (1 << s) + 1][s]);
				if (ma - mi > k) {
					ans += n - j + 1;
					break;
				}
				j++;
			}
		}
		cout << ans << "\n";
	}

	return 0;
}





2021牛客暑期多校训练营6


I.Intervals on the Ring

题意:给出环上的一组区间,你需要构造环上的一组区间使得这些区间的交是给定的区间的并。
集合的德摩根律告诉我们区间的并的补等于区间的补的交

所有答案=空白区间并的补集=空白区间补的交集
所以你要给定的区间就是空白区间的补集

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e3 + 10;
const ll mod = 1e9 + 7;
const ll inf = 0x3f3f3f3f3f3f3f3f;

int n, T, m;
pair<int, int> arr[maxn];

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	cin >> T;
	while (T--) {
		cin >> n >> m;
		for (int i = 0; i < m; i++)
			cin >> arr[i].first >> arr[i].second;
		sort(arr, arr + m);
		cout << m << "\n";
		for (int i = 0; i < m; i++) {
			cout << arr[i].first << " " << arr[(i - 1 + m) % m].second << "\n";
		}
	}
	return 0;
}





F.Hamburger

题意:有n个汉堡,m个锅,每个汉堡煮熟需要 t i t_i ti的时间,一个锅每次只能煮一个汉堡,一个汉堡最多只能经过两个锅。
思维题,首先一个汉堡一次只能在一个锅里,所以不管他经过了几个锅,做完这个汉堡的总时间为时间 t i t_i ti,所以最小时间 > = m a x ( t i ) >=max(t_i) >=max(ti),但是 ∑ i = 1 n t i ≤ m ∗ m a x ( t i ) \sum_{i=1}^n t_i \leq m*max(t_i) i=1ntimmax(ti)不一定成立!所以我们还需要找到他的最坏情况,即 ( ∑ i = 1 n t i ) / m + ( ( ∑ i = 1 n t i ) % m = = 0 ) (\sum_{i=1}^n t_i)/m+((\sum_{i=1}^n t_i)\%m==0) (i=1nti)/m+((i=1nti)%m==0)
找到每个锅的上限,那么就可以贪心的往锅里放汉堡了。

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e5 + 10;
const ll mod = 1e9 + 7;
const ll inf = 0x3f3f3f3f3f3f3f3f;

ll n, m, sum, mmax, t[maxn];

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> t[i];
		sum += t[i];
		mmax = max(mmax, t[i]);
	}
	sum = sum % m == 0 ? sum / m : sum / m + 1;
	mmax = max(mmax, sum);
	ll num = 1;
	ll vnum = 0;
	for (int i = 1; i <= n; i++) {
		if (t[i] + vnum <= mmax)
			cout << 1;
		else cout << 2;

		
		if (t[i] + vnum <= mmax) {
			cout << " " << num << " " << vnum << " " << vnum + t[i];
			vnum += t[i];
			t[i] = 0;
		}
		else {
			cout << " " << num + 1 << " " << 0 << " " << (t[i] - mmax + vnum);
			cout << " " << num << " " << vnum << " " << mmax;
			vnum =t[i]-mmax+vnum ; num++;
			t[i] = 0;
		}
		if (vnum == mmax) {
			num++;
			vnum = 0;
		}
		
		cout << "\n";
	}

	return 0;
}


2021牛客暑期多校训练营7


H.Xay loves count

题意:给你 n n n个数字,询问有多少三元组 ( i , j , k ) (i,j,k) (i,j,k)满足 a i ∗ a j = a k a_i*a_j=a_k aiaj=ak
枚举两个数 a i , a j a_i,a_j ai,aj,那么 a k a_k ak我们就已知了,当 a i a_i ai固定, a j a_j aj最坏情况有 ⌊ 1 e 6 a i ⌋ \lfloor \frac{1e6}{a_i} \rfloor ai1e6种,那么总复杂度为
∑ i = 1 1 e 6 ⌊ 1 e 6 a i ⌋ \sum _{i=1}^{1e6}\lfloor \frac{1e6}{a_i} \rfloor i=11e6ai1e6

签到题,刚开始看到的时候被假 n 2 n^2 n2的复杂度吓到了,没有想到调和级数,后来悟了,看似 n 2 n^2 n2其实最多才1e7左右(比赛的时候打表算的,就是算的上面那个公式,好像是1e7)

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e6 + 10;
const ll mod = 1e9 + 7;
const ll inf = 0x3f3f3f3f3f3f3f3f;
ll n, x, cnt;
ll arr[maxn], num[maxn];// 表示x出现过几次
bool vis[maxn];// 标记数x是否出现过
int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> x;
		if (vis[x]) {
			num[x]++;
		}
		else {
			vis[x] = true;
			num[x] = 1;
			arr[++cnt] = x;
		}
	}
	sort(arr + 1, arr + 1 + cnt);
	ll ans = 0;
	for (int i = 1; i <= cnt; i++) {
		for (int j = 1; j <= cnt; j++) {
			if (arr[i] * arr[j] > arr[cnt])
				break;
			ans += num[arr[i]] * num[arr[j]] * num[arr[i] * arr[j]];
		}
	}
	cout << ans;

	return 0;
}





I.Xay loves or

题意:询问有多少个 y y y,满足 x ∣ y = s x|y=s xy=s
简单题,当时读题有问题,没有看到 y y y是正整数,所以 y ! = 0 y!=0 y!=0,这个要特判一下

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e6 + 10;
const ll mod = 1e9 + 7;
const ll inf = 0x3f3f3f3f3f3f3f3f;
ll x, s;
int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	cin >> x >> s;
	if (x > s) {
		cout << 0;
		return 0;
	}
	bool sy = 0;
	if (s == x)
		sy = 1;
	ll ans = 1;
	while (s) {
		int ts = s & 1;
		s >>= 1;
		int tx = x & 1;
		x >>= 1;
		if (ts == 0 && tx == 1) {
			cout << 0;
			return 0;
		}
		if (ts == 1 && tx == 1) {
			ans *= 2;
		}
	}
	if (sy)
		ans--;
	cout << ans;
	return 0;
}





2021牛客暑期多校训练营8


D.OR

题意:给你两个长度为 n − 1 n-1 n1非负数组( b , c [ 2 , n ] b,c_{[2,n]} b,c[2,n])
b i = a i ∣ a i − 1 , c i = a i + a i − 1 b_i=a_i|a_{i-1},c_i=a_i+a_{i-1} bi=aiai1,ci=ai+ai1
让你求满足 b c bc bc a a a有多少种?

这题的思路感觉跟牛客第四场的E思路很像,通过 c i = a i + a i − 1 c_i=a_i+a_{i-1} ci=ai+ai1,如果 a 1 a_1 a1确定,那么序列 a a a的其他数也就确定了,所以答案就是看有多少种 a 1 a_1 a1是满足条件的
但是这个题有没有那个题那样麻烦,因为 a i + a i − 1 = a i ∣ a i − 1 + a i & a i − 1 a_i+a_{i-1}=a_i|a_{i-1}+a_i\&a_{i-1} ai+ai1=aiai1+ai&ai1,通过每一位的与/或,我们就可以很轻松的确定一个二进制位上的数是0还是1
我们可以所有所有的 a 1 a_1 a1的二进制位的可能性
复杂度 O ( m ∗ n ) O(m*n) O(mn), m m m a 1 a_1 a1的二进制最高位数

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e5 + 10;
const ll mod = 1e9 + 7;
const ll inf = 0x3f3f3f3f3f3f3f3f;

int n;
int b[maxn];//a[i-1]|a[i]
int c[maxn];//a[i-1]+a[i]
int d[maxn];//a[i-1]&a[i]
int sum;
inline void dfs(int lastnum, int nowbit,int i) {
	if (i >= n + 1) {
		sum++;
		return;
	}
	if (((d[i]>>nowbit)&1) == 1) {
		if (lastnum == 0)
			return;
		if (((b[i] >> nowbit)&1) == 0)
			return;
		dfs(1, nowbit, i + 1);
	}
	else {
		if (((b[i] >> nowbit)&1) == 1) {
			dfs(!lastnum, nowbit, i + 1);
		}
		else {
			if (lastnum)
				return;
			dfs(0, nowbit, i + 1);
		}
	}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif


	cin >> n;
	for (int i = 2; i <= n; i++)
		cin >> b[i];// a[i-1]|a[i]
	for (int i = 2; i <= n; i++) {
		cin >> c[i];
		d[i] = c[i] - b[i]; // a[i-1]&a[i]
	}
	ll ans = 1;
	// 因为a,b,c全为0的sum=1,所以不用特意去算a1的最高位,直接使用a能达到的最高位就行
	for (int i = 0; i < 31; i++) {
		dfs(0, i,2);
		dfs(1, i,2);
		ans *= sum;
		sum = 0;
	}
	cout << ans;
	return 0;
}





F.Robots

bitset+离线化处理+滚动
题意:一个机器人会有三种操作,其中第一种和第二种构成了第三种操作,那如果我们知道了通过第三种操作能到达的所有点,那么判一下是不是在同一列或者同一行里不就可以判断能不能通过第一或第二种操作到达了么!(因为一列中有障碍物,他不可能通过第三种方法到达,因为他只能往下和往右走!)

那么我们现在的需求很明确,找到一个点对应的所有可到达的情况,就是要构造一个可达图!
四维矩阵(我还真想过)需要的内存将近 40 G 40G 40G,那必不可能
我们想如何才能优化这个可达图,让我们能够存下它来,我们可以采取类似滚动数组的方法, ( i , j ) (i,j) (i,j)的状态可以利用 ( i , j − 1 ) (i,j-1) (i,j1) ( i − 1 , j ) (i-1,j) (i1,j)的状态
为什么可以呢?
因为到达 ( i , j ) (i,j) (i,j)只能通过两个点到达,即 ( i − 1 , j ) , ( i , j − 1 ) (i-1,j),(i,j-1) (i1,j),(i,j1),所以我们存图只需要存一行的,直接四位数组变三维数组,但是滚动之后。
滚过去的点就没法用了,怎么办?
我们先把所有询问存储下来,离线化处理完所有答案,然后再输出!
为什么要用bitset,因为复杂度是 n 2 n^2 n2次大小为 n 2 n^2 n2的数组的重载,bitset要比数组快32倍?听说的

O ( n 4 ) 优 化 的 近 似 于 O ( n 3 l o g n ) O(n^4)优化的近似于O(n^3logn) O(n4)O(n3logn)

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 510;
const ll mod = 1e9 + 7;
const ll inf = 0x3f3f3f3f3f3f3f3f;

int n, m;
struct Node {
	int t, x, y, step;
	Node(int tt,int xx,int yy,int sst) {
		t = tt, x = xx, y = yy, step = sst;
	}
};
bitset<maxn* maxn> bt[maxn];
int mp[maxn][maxn];
vector<Node> no[maxn][maxn];
int ans[1000000];

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			scanf("%1d", &mp[i][j]);
		}
	}
	int t, x1, y1, x2, y2, q;
	cin >> q;
	for(int i=1;i<=q;i++){
		cin >> t >> x1 >> y1 >> x2 >> y2;
		no[x2][y2].emplace_back(t, x1, y1, i);
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			if (!mp[i][j]) {
				bt[j] |= bt[j - 1];
				bt[j][(i - 1) * m + j] = 1;
			}
			else {
				bt[j].reset();
			}
			for (auto k : no[i][j]) {
				if (k.t == 1) {
					if (j != k.y) {
						ans[k.step] = 0;
						continue;
					}
				}
				else if (k.t == 2) {
					if (i != k.x) {
						ans[k.step] = 0;
						continue;
					}
				}
				if (i < k.x || j < k.y) {
					ans[k.step] = 0;
					continue;
				}
				ans[k.step] = bt[j][(k.x - 1) * m + k.y];
			}
		}
	}
	for (int i = 1; i <= q; i++) {
 		cout << (ans[i] == 1 ? "yes\n" : "no\n");
	}

	return 0;
}





K.Yet Another Problem About Pi

题意:给你一个长宽为 w , d w,d w,d的小矩形拼成的无限大的图,问你你最多能经过几个小方格,总路径为3.1415926···

最优情况肯定是由延边走和延斜线走,那么枚举一下走几个边线就行

#define _CRT_SECURE_NO_WARNINGS
/***
*                    .::::.
*                  .::::::::.
*                 :::::::::::
*             ..:::::::::::'
*           '::::::::::::'
*             .::::::::::
*        '::::::::::::::..
*             ..::::::::::::.
*           ``::::::::::::::::
*            ::::``:::::::::'        .:::.
*           ::::'   ':::::'       .::::::::.
*         .::::'      ::::     .:::::::'::::.
*        .:::'       :::::  .:::::::::' ':::::.
*       .::'        :::::.:::::::::'      ':::::.
*      .::'         ::::::::::::::'         ``::::.
*  ...:::           ::::::::::::'              ``::.
* ````':.          ':::::::::'                  ::::..
*                    '.:::::'                    ':'````..
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll maxn = 1e6 + 10;
const ll mod = 1e9 + 7;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0);
ll T;
double w, d;

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	cin >> T;
	while (T--) {
		cin >> w >> d;
		if (w > d)
			swap(w, d);
		double li = sqrt(w * w + d * d);
		ll ans = 0;
		for (int i = 0; i < 3; i++) {
			if (i * w <= PI) {
				ans = max(ans, (ll)((i * 2) + 4 + (ll)((PI - i * w) / li) * 3));
			}
			if (i * li <= PI) {
				ans = max(ans, (ll)((i * 3) + 4 + (ll)((PI - i * li) / w) * 2));
			}
		}
		cout << ans << "\n";
	}


	return 0;
}





  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LOTRcsl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值