2019牛客暑期多校训练营(第一场)

A B C E F H I J 已补   D G未补

A.Equivalent Prefixes

赛场上n^2过掉,简直不可思议。正确的解法就是维护当前这对数能影响的左右位置的区间范围。这可以用笛卡尔树或者单调栈实现。

笛卡尔树版:

#include <iostream>
#include <map>
#include <cstring>
#include <queue>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
#define min(a,b) ((a)<(b)?(a):(b))
const int N = 100010;
int n;
int ax[100010];
int bn[100010];
struct Cartesian_Tree {
	struct node {
		int id, val, fa;
		int son[2];
		node() {}
		node(int id, int val, int fa) : id(id), val(val), fa(fa) {
			son[0] = son[1] = 0;
		}
	}t[N];
	int root,l[N],r[N];
	void init() {
		t[0] = node(0, 0, 0);
	}
	void build(int n, int *a) {
		for (int i = 1; i <= n; ++i) {
			t[i] = node(i, a[i], 0);
		}
		for (int i = 1; i <= n; ++i) {
			int k = i - 1;
			while (t[k].val > t[i].val) {
				k = t[k].fa;
			}
			t[i].son[0] = t[k].son[1];
			t[k].son[1] = i;
			t[i].fa = k;
			t[t[i].son[0]].fa = i;
		}
		root = t[0].son[1];
	}
	int DFS(int u) {
		if (u == 0) return 0;
		l[t[u].id] = DFS(t[u].son[0]);
		r[t[u].id] = DFS(t[u].son[1]);
		return l[t[u].id] + r[t[u].id] + 1;
	}
};
Cartesian_Tree t1,t2;
int check(int x) {
	t1.init();
	t1.build(x, ax);
	t2.init();
	t2.build(x, bn);
	t1.DFS(t1.root);
	t2.DFS(t2.root);
	for (int i = 1; i <= x; i++) {
		if (t1.l[i] != t2.l[i] || t1.r[i] != t2.r[i]) {
			return 0;
		}
	}
	return 1;
}
int main(void) {
	while (scanf("%d", &n) != EOF) {
		for (int i = 1; i <= n; i++) {
			scanf("%d", &ax[i]);
		}
		for (int i = 1; i <= n; i++) {
			scanf("%d", &bn[i]);
		}
		int left = 1;
		int right = n;
		while (left <= right) {
			int mid = (left + right) / 2;
			if (check(mid)) {
				left = mid + 1;
			}
			else {
				right = mid - 1;
			}
		}
		printf("%d\n", left - 1);
	}
}

单调栈标程:

#include <iostream>
#include <map>
#include <cstring>
#include <queue>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
#define min(a,b) ((a)<(b)?(a):(b))

std::vector<int> read(int n) {
	std::vector<int> result(n);
	std::vector<std::pair<int, int>> stack;
	stack.emplace_back(0, -1);
	for (int i = 0, a; i < n; ++i) {
		scanf("%d", &a);
		while (stack.back().first > a) {
			stack.pop_back();
		}
		result[i] = stack.back().second;
		stack.push_back(make_pair(a, i));
	}
	return result;
}

int main() {
	int n;
	while (scanf("%d", &n) == 1) {
		auto a = read(n);
		auto b = read(n);
		int length = 0;
		while (length < n && a[length] == b[length]) {
			length++;
		}
		printf("%d\n", length);
	}
}

B:

简单推公式,因为我们无法处理相乘的式子的积分,因为只有相加的式子才可以分开积分,所以首先我们要将式子拆分开,推出一般式,然后再简单的换元一下即可。

#include <iostream>
#include <map>
#include <cstring>
#include <queue>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
#define min(a,b) ((a)<(b)?(a):(b))
const int mod = 1e9 + 7;
long long ax[1100];
long long pow_mod(long long a, long long n, long long m){
	if (n == 0) return 1;
	long long x = pow_mod(a, n / 2, m);
	long long ans = (long long)x * x % m;
	if (n % 2 == 1) ans = ans * a % m;
	return ans;
}
int main(void) {
	int n;
	while (scanf("%d", &n) != EOF) {
		for (int i = 0; i < n; i++) {
			scanf("%lld", &ax[i]);
		}
		long long ans = 0;
		for (int i = 0; i < n; i++) {
			long long v = 2 * ax[i]%mod;
			long long cnt = 1;
			for (int z = 0; z < n; z++) {
				if (i == z) continue;
				cnt = cnt * ((ax[z] * ax[z] % mod - ax[i] * ax[i]%mod) % mod+mod)%mod;
			}
			ans =( ans + pow_mod(cnt*v%mod, mod - 2, mod)%mod)%mod;
		}
		printf("%lld\n", ans);
	}
}

C

可以构造出一个拉格朗日函数,但是无奈我看不懂题解,无法将这个n元的函数求解。所以换了别人的思路,首先我们要知道当n大于0时,每当n越大,n减少一点带来的n^2贡献也会越大,这样就很明显了,我们现在总共只有1的费用,所以我们肯定会贪心的让整个序列n^2的最大值尽可能小。所以只要维护一个前缀和,寻找尽可能的最大值即可。

#include <iostream>
#include <map>
#include <cstring>
#include <queue>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
#define maxn 10050
long long ax[maxn];
long long gcd(long long a, long long b){
	long long r;
	while (b > 0){
		r = a % b;
		a = b;
		b = r;
	}
	return a;
}

int main(void) {
	int n, m;
	while (scanf("%d%d", &n, &m) != EOF) {
		for (int i = 0; i < n; i++) {
			scanf("%lld", &ax[i]);
		}
		sort(ax, ax + n);
		long long sum = ax[n - 1];
		int i;
		long long suma = 0;
		long long sumb = 0;
		for (i = n - 2; i >= 0; i--) {
			if (sum - (n - 1 - i)*ax[i] > m) {
				break;
			}
			else if (sum - (n - 1 - i)*ax[i] <= m) {
				sum += ax[i];
			}
		}
		sum = sum - m;
		suma = suma + sum * sum;
		sumb = n - 1 - i;
		for (; i >= 0; i--) {
			suma = suma + ax[i] * ax[i] * sumb;
		}
		sumb = sumb * m * m;
		long long h = gcd(suma, sumb);
		suma /= h;
		sumb /= h;
		if (sumb == 1||suma==0) {
			printf("%lld\n", suma);
		}
		else printf("%lld/%lld\n", suma, sumb);
	}
	return 0;
}

D.

据说是FWT  未补

E.

因为你在当前位置放下的A会对之前放下的B和之后放下的B产生贡献,当前放下的B会对之前放下的A和之后放下的A产生贡献,所以就可以按照这种方式进行DP。

你在当前位置能放下A,是因为构成AB的数量不够或者构成BA的数量不够。

你在当前位置能放下B,是因为构成BA的数量不够或者构成AB的数量不够。

按照这种方式递推即可,懒得写递推式了。

#include <iostream>
#include <map>
#include <cstring>
#include <queue>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
#define maxn 10050
const int mod = 1e9 + 7;
long long dp[2200][2200];
int main(void) {
	int n, m;
	while (scanf("%d%d", &n, &m) != EOF) {
		for (int i = 0; i <= n + m; i++) {
			for (int z = 0; z <= n + m; z++) {
				dp[i][z] = 0;
			}
		}
		dp[0][0] = 1;
		for (int i = 0; i <=(n + m); i++) {
			for (int z = 0; z <=(n + m); z++) {
				if (i < n || i - n < z) {
					dp[i + 1][z] += dp[i][z];
					dp[i + 1][z] %= mod;
				}
				if (z < m || z - m < i) {
					dp[i][z + 1] += dp[i][z];
					dp[i][z + 1] %= mod;
				}
			}
		}
		printf("%lld\n", dp[n+m][m+n]);
	}
}

F.

在三角形内随机取点,求三角形分割的最大面积的期望。

答案是11/36*S。可以积分求出

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<set>
#include<vector>
#include<map>
#include<cmath>
using namespace std;
typedef long long LL;
 
double p,s;
 
 
 
int main(void) {
    LL x1,y1, x2, y2, x3, y3;
    while (cin>>x1>>y1>>x2>>y2>>x3>>y3)
    {
        LL a=x2-x1,b=y2-y1;
        LL c=x3-x1,d=y3-y1;
        LL ans=abs(a*d-b*c);
        printf("%lld\n", ans*11 );
    }
    return 0;
}

 

G.

未补

 

H.

求序列内的所有集合异或和等于0的集合大小和

看了好多题解,第一次接触线性基,费力理解。

首先集合大小和可以转换为序列中每个元素的参与贡献和。

第一步:我们先求出整个序列的线性基(大小为r),对于线性基外的元素,他们的每个集合的异或和都可以用线性基内的元素表示出来,每个元素参与贡献为2^(n-r-1).总贡献为(n-r)*2^(n-r-1).

第二步:算线性基里面元素的贡献,对于线性基里面的一个数,如果他能被另外n-1个数表示出来,那就证明存在另外一个线性基(线性基大小相等),而它是处于线性基外的元素,贡献也为2^(n-r-1)。依次判断线性基内的每个元素即可。

#include <iostream>
#include <map>
#include <cstring>
#include <queue>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
#define maxn 100010
const int mod = 1e9 + 7;
struct Linear_Basis{
	LL b[63], nb[63], tot;

	void init(){
		tot = 0;
		memset(b, 0, sizeof(b));
		memset(nb, 0, sizeof(nb));
	}

	bool ins(LL x){
		for (int i = 62; i >= 0; i--)
			if (x&(1LL << i)){
				if (!b[i]) { b[i] = x; tot++; break; }
				x ^= b[i];
			}
		return x > 0;
	}

	LL Max(LL x){
		LL res = x;
		for (int i = 62; i >= 0; i--)
			res = max(res, res^b[i]);
		return res;
	}

	LL Min(LL x){
		LL res = x;
		for (int i = 0; i <= 62; i++)
			if (b[i]) res ^= b[i];
		return res;
	}

	void rebuild(){
		for (int i = 62; i >= 0; i--)
			for (int j = i - 1; j >= 0; j--)
				if (b[i] & (1LL << j)) b[i] ^= b[j];
		for (int i = 0; i <= 62; i++)
			if (b[i]) nb[tot++] = b[i];
	}

	LL Kth_Max(LL k){
		LL res = 0;
		for (int i = 62; i >= 0; i--)
			if (k&(1LL << i)) res ^= nb[i];
		return res;
	}

} LB;
long long pow_mod(long long a, long long n, long long m){
	if (n == 0) return 1;
	long long x = pow_mod(a, n / 2, m);
	long long ans = (long long)x * x % m;
	if (n % 2 == 1) ans = ans * a % m;
	return ans;
}
LL ax[maxn];
int vis[maxn];
vector<LL>v;
int main(void) {
	int n;
	Linear_Basis B1, B2, B3;
	while(scanf("%d",&n)!=EOF){
		v.clear();
		B1.init();
		B2.init();
		B3.init();
		for (int i = 0; i < n; i++) {
			scanf("%lld", &ax[i]);
			vis[i] = 0;
		}
		int cnt = 0;
		LL ans = 0;
		for (int i = 0; i < n; i++) {
			if (B1.ins(ax[i])) {
				vis[i] = 1;
				cnt++;
				v.push_back(ax[i]);
			}
		}
		ans = (ans + (n-cnt)*pow_mod(2,n-cnt-1,mod)%mod) % mod;
		for (int i = 0; i < n; i++) {
			if (!vis[i]) {
				B2.ins(ax[i]);
			}
		}
		for (int i = 0; i < v.size(); i++) {
			B3 = B2;
			for (int z = 0; z < v.size(); z++) {
				if (i == z) continue;
				else {
					B3.ins(v[z]);
				}
			}
			if(!B3.ins(v[i]))
				ans = (ans + pow_mod(2, n - cnt - 1, mod) % mod) % mod;
		}
		printf("%lld\n", ans);
	}
	return  0;
} 

I.

队友补的

J.

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<iostream>
#include<map>
#include<vector>
#include<set>
#include<queue>
#include<stack>
using namespace std;
typedef long long ll;
 
ll x, y, a, b;
 
int main()
{
    while (scanf("%lld %lld %lld %lld", &x, &a, &y, &b) != EOF)
    {
        ll fz = b * x - a * y;
        ll fm = a * b;
        if (fz == 0)
        {
            printf("=\n");
        }
        else if (fm > 0 && fz > 0 || fm < 0 && fz < 0)
        {
            printf(">\n");
        }
        else
        {
            printf("<\n");
        }
    }
 
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值