ABC366

A

link

判断一下少的那个人加上剩下的所有票是否会超过另一个人,如果超过,不确定,否则目前票多的必胜。

神奇的代码
#include<bits/stdc++.h>

using namespace std;

signed main(){
	
	int n,a,b;
	cin >> n >> a >> b;
	int s = n-a-b;
	if(a < b){
		if(a+s > b){
			cout << "No";
		}
		else cout << "Yes";
	}
	else{
		if(b+s > a){
			cout << "No";
		}
		else cout << "Yes";
	}
	
	return 0;
	
}

B

link

我并没有看下面讲解只看了描述(竖线中的)
略一看样例便可知道,是一列一列从下往上输出,对于一列,如果这个位置有字符,输出,否则,如果后面(也就是上面)还有字符,输出一个*,否则break
那么我们可以存一下每一列最靠后的字符(也就是最靠上的字符),如果超出了这个,就结束。
别忘了存一下最大长度。

神奇的代码
#include<bits/stdc++.h>

using namespace std;

int n;
string s[105];
int w[105],mx;

signed main(){
	
	cin >> n;
	for(int i = 1;i <= n;++ i){
		cin >> s[i];
		int sn = s[i].length();
		if(sn > mx) mx = sn;
		for(int j = 0;j < s[i].length();++ j)
			w[j] = (w[j] == 0)?i:w[j];
	}
	
	for(int i = 0;i < mx;++ i){
		for(int j = n;j >= 1&&j >= w[i];-- j){
			int sn = s[j].length();
			if(sn <= i) cout << '*';
			else cout << s[j][i];
		}
		cout << endl;
	}
	
	return 0;
	
}

C

link

我觉得C<B
开一个桶,如果是一个新的,个数加一,如果减没了,个数减一。

神奇的代码
#include<bits/stdc++.h>

using namespace std;

int q;
int x,tp;
int bucket[1000005],sum;

signed main(){
	
	cin >> q;
	for(int i = 1;i <= q;++ i){
		cin >> tp;
		if(tp == 1){
			cin >> x;
			bucket[x]++;
			if(bucket[x] == 1) sum++;
		}
		else if(tp == 2){
			cin >> x;
			bucket[x]--;
			if(bucket[x] == 0) sum--;
		}
		else cout << sum << endl;
	}
	
	return 0;
	
}

D

link

三维前缀和。这里提供两种思考方式。

直接想三维前缀和

考虑类比二位前缀和的容斥原理,脑补一下,反正我没脑补出来图,奇加偶减(具体可以学习一下容斥原理),那么就有\(qzh_{i,j,k}=qzh_{i-1,j,k}+qzh_{i,j-1,k}+qzh_{i,j,k-1}-qzh_{i-1,j-1,k}-qzh_{i-1,j,k-1}-qzh_{i,j-1,k-1}+qzh_{i-1,j-1,k-1}+a_{i,j,k}\),查询也是类似的(详见代码)。
这个容斥原理的适用于所有维前缀和。

神奇的代码
#include<bits/stdc++.h>

using namespace std;

int n;
int q;
int a[105][105][105];
int qzh[105][105][105];

signed main(){
	
	cin >> n;
	for(int i = 1;i <= n;++ i)
		for(int j = 1;j <= n;++ j)
			for(int k = 1;k <= n;++ k){
				cin >> a[i][j][k];
				qzh[i][j][k] = qzh[i-1][j][k]+qzh[i][j-1][k];
				qzh[i][j][k] += qzh[i][j][k-1];
				qzh[i][j][k] -= qzh[i-1][j-1][k];
				qzh[i][j][k] -= qzh[i-1][j][k-1];
				qzh[i][j][k] -= qzh[i][j-1][k-1];
				qzh[i][j][k] += qzh[i-1][j-1][k-1]+a[i][j][k];
			}
	
	cin >> q;
	while(q--){
		int li,ri,lj,rj,lk,rk;
		cin >> li >> ri >> lj >> rj >> lk >> rk;
		int ans = qzh[ri][rj][rk];
		ans -= qzh[li-1][rj][rk];
		ans -= qzh[ri][lj-1][rk];
		ans -= qzh[ri][rj][lk-1];
		ans += qzh[li-1][lj-1][rk];
		ans += qzh[li-1][rj][lk-1];
		ans += qzh[ri][lj-1][lk-1];
		ans -= qzh[li-1][lj-1][lk-1];
		cout << ans << endl;
	}
	
	return 0;
	
}

考虑分层

考虑按高度分层,分\(n\)层,那么每一层都是一个二位前缀和,求完所有层后,把层按一维前缀和的思路加上,查询答案的时候也按二维前缀和的思路查,只不过每一步都是把要的那几层取出来算(详见代码)。
这个思路只是一时兴起,但是是降维作用的。

神奇的代码
#include<bits/stdc++.h>

using namespace std;

int n;
int q;
int a[105][105][105];
int qzh[105][105][105];

signed main(){
	
	cin >> n;
	for(int i = 1;i <= n;++ i){
		for(int j = 1;j <= n;++ j){
			for(int k = 1;k <= n;++ k){
				cin >> a[i][j][k];
				qzh[i][j][k] = qzh[i-1][j][k]+qzh[i][j-1][k];
				qzh[i][j][k] -= qzh[i-1][j-1][k];
				qzh[i][j][k] += a[i][j][k];
			}
		}
	}
	for(int i = 1;i <= n;++ i)
		for(int j = 1;j <= n;++ j)
			for(int k = 1;k <= n;++ k)
				qzh[i][j][k] += qzh[i][j][k-1];
	
	cin >> q;
	while(q--){
		int li,ri,lj,rj,lk,rk;
		cin >> li >> ri >> lj >> rj >> lk >> rk;
		int ans = qzh[ri][rj][rk]-qzh[ri][rj][lk-1];
		int jian = qzh[ri][lj-1][rk]-qzh[ri][lj-1][lk-1];
		ans -= jian;
		jian = qzh[li-1][rj][rk]-qzh[li-1][rj][lk-1];
		ans -= jian;
		jian = qzh[li-1][lj-1][rk]-qzh[li-1][lj-1][lk-1];
		ans += jian;
		cout << ans << endl;
	}
	
	return 0;
	
}

E

link

我们把它当做一个数学题,光考虑式子,不考虑图形。
首先\(\displaystyle \sum_{i=1}^{n} (|x-x_i|+|y-y_i|)=\displaystyle \sum_{i=1}^{n}|x-x_i|+\displaystyle \sum_{i=1}^{n}|y-y_i|\leq D\),我们发现\(D\leq 10^6\),所以枚举\(D\)是可行的,那么,我们枚举\(t=0\)~\(D\),令\(\displaystyle \sum_{i=1}^{n}|x-x_i|=t\),那么\(\displaystyle \sum_{i=1}^{n}|y-y_i|\)就要\(\leq D-t\),那么我们求出\(\displaystyle \sum_{i=1}^{n}|x-x_i|=t\)\(x\)的个数,再求出\(\displaystyle \sum_{i=1}^{n}|y-y_i|\leq D-t\)\(y\)的个数,相乘即为这个\(t\)对答案的贡献,那么问题转化为求\(\displaystyle \sum_{i=1}^{n}|x-x_i|=t\)\(x\)的个数和\(\displaystyle \sum_{i=1}^{n}|y-y_i|\leq D-t\)\(y\)的个数。
先把\(\displaystyle \sum_{i=1}^{n}|y-y_i|\leq D-t\)\(y\)个数转换一下,可以转换成\(\displaystyle \sum_{i=1}^{n}|y-y_i|\)等于某个数的个数,再求一个前缀和,就有等于\(0\)~这个数的个数,也就是小于等于这个数的个数啦。那么现在两个个数形式一样了,都是\(\displaystyle \sum_{i=1}^{n}|x-x_i|=t\)的个数。
问题就转化为了求\(\displaystyle \sum_{i=1}^{n}|x/y-x/y_i|=t\)\(x/y\)的个数,以\(x\)为例,我们大致算一下\(x\)的范围,是\(-2\times 10^6\)~\(2\times 10^6\),因为\(D\)\(10^6\)的,\(x_i\)\(-10^6\)~\(10^6\)的,如果超过这个范围就与\(x_i\)的差无论如何也不会小于\(D\)\(10^6\))了,那么我们又发现可以枚举\(x\)
枚举了\(x\),我们就需要\(O(1)\)知道\(\displaystyle \sum_{i=1}^{n}|x-x_i|\)的值,我们拆一下绝对值,想象一个数轴,有一些\(x_i\)和一个\(x\),那么假设有\(t\)\(x_i\)\(x\)之前,那么这\(t\)\(x_i\)拆完绝对值就是被\(x\)减去,其他的\(n-t\)个就是减去\(x\),那么最终就是\(\displaystyle \sum_{i=1}^{t}(x-x_i)+\displaystyle \sum_{i=t+1}^{n}(x_i-x)\),再拆一下,\(tx-\displaystyle \sum_{i=1}^{t}x_i+\displaystyle \sum_{i=t+1}^{n}x_i-(n-t)x = (2t-n)x-\displaystyle \sum_{i=1}^{t}x_i+\displaystyle \sum_{i=t+1}^{n}x_i\)对于同一个\(t\),后面两项是相同的,只是前面在变,那么我们可以枚举每一个段(\(x_i\)\(x_{i+1}\)中间的数,这里左闭右开),再枚举里面的数,他们的后两项相同,只改变前一项即可,这样存一下后两项的值,在交界处改变一个即可算出。

神奇的代码
#include<bits/stdc++.h>

#define int long long

using namespace std;

int n,d;
int x[200005],y[100005];
int f[1000005],g[1000005];
int ans;

void suan(int a[],int w[]){
	sort(a+1,a+1+n);
	a[0] = -2e6,a[n+1] = 2e6+1;
	int q = 0,h = 0;
	for(int i = 1;i <= n;++ i) h += a[i];
	for(int i = 0;i <= n;++ i){
		if(i) q += a[i],h -= a[i];
		for(int j = a[i];j < a[i+1];++ j){
			int x = (2*i-n)*j-q+h;
			if(x <= d) w[x]++;
		}
	}
}

signed main(){
	
	cin >> n >> d;
	for(int i = 1;i <= n;++ i) cin >> x[i] >> y[i];
	
	suan(x,f);suan(y,g);
	
	for(int i = 1;i <= d;++ i) g[i] += g[i-1];
	for(int i = 0;i <= d;++ i) ans += f[i]*g[d-i];
	cout << ans;
	
	return 0;
	
}

F

link

首先考虑顺序问题,假设就是把这\(n\)个函数排个序,使得算出来的数最大。简单推理一下。

首先考虑一下从\(1\)\(n\)(这里我们说从什么到什么默认是从里到外)的顺序答案是什么。先以\(4\)个的为例,\(A_4(A_3(A_2(A_1+B_1)+B_2)+B_3)+B_4\),拆括号,\(A_1A_2A_3A_4+B_1A_2A_3A_4+B_2A_3A_4+B_3A_4+B_4\),看出规律了吗?\(\displaystyle{\prod_{w=1}^{n}}A_w+\displaystyle{\sum_{k=1}^{n}}(B_k\displaystyle{\prod_{w=k+1}^{n}}A_w)\)(这个式子十分没有用,看前面那个足矣)。
考虑\(A_i\)\(A_{i+1}\)的顺序对答案的影响,先考虑它们的顺序对哪几项有影响(我们对项的标号以\(B_i\)\(i\)为准,第一项我们标为第\(0\)项,我们可以认为\(B_0=1\)),发现只有第\(i\)项和第\(i+1\)项受影响,\(i-1\)之前的没有\(B_i\)\(B_{i+1}\),有\(A_i\)\(A_{i+1}\)交换后不受影响,\(i+2\)之后的\(A\)\(B\)\(i\)\(i+1\)就都不含了,也没有影响,\(i\)\(i+1\)项只看\(B\)就肯定会受影响。交换后本来\(B_i\)乘的变成了\(B_{i+1}\)乘,本来\(B_{i+1}\)乘的变成了\(B_i\)乘,肯定会受影响。(有点啰嗦。。。)
再看受了啥影响。之前的是\(B_iA_{i+1}A_{i+2}A_{i+3}……+B_{i+1}A_{i+2}A_{i+3}……\),后来的是\(B_{i+1}A_iA_{i+2}A_{i+3}……+B_iA_{i+2}A_{i+3}……\)\(i\)\(i+1\)换一下),如果前面的小于后面的那么换了就更优(\(B_iA_{i+1}A_{i+2}A_{i+3}……+B_{i+1}A_{i+2}A_{i+3}……<B_{i+1}A_iA_{i+2}A_{i+3}……+B_iA_{i+2}A_{i+3}……\)),但是这个式子太长了,化简一下。
首先把\(A_{i+2}A_{i+3}……\)约掉,\(B_iA_{i+1}+B_{i+1}<B_{i+1}A_i+B_i\),这个时候已经可以排序了(写在\(cmp\)函数里),但是我继续化简了一下,\(B_iA_{i+1}+B_{i+1}<B_{i+1}A_i+B_i\to B_iA_{i+1}-B_i<B_{i+1}A_i-B_{i+1}\to B_i(A_{i+1}-1)<B_{i+1}(A_i-1)\)

那么现在我们只要在这个顺序里选\(k\)个即可。
考虑\(dp\),用\(f_{i,j}\)代表考虑到\(i\),选了\(j\)个的最大函数值。考虑转移,如果第\(i\)个不选,那么就是\(f_{i-1,j}\),如果选,就是\(f_{i-1,j-1}*A_i+B_i\),取\(max\)即可。

神奇的代码
#include<bits/stdc++.h>

#define int long long

using namespace std;

int n,k;
int f[200005][15]; 
struct nd{
	int a,b;
}x[200005];

bool cmp(nd l,nd r){
	return l.b*(r.a-1) > r.b*(l.a-1);
}

signed main(){
	
	cin >> n >> k;
	for(int i = 1;i <= n;++ i)
		cin >> x[i].a >> x[i].b;
	sort(x+1,x+1+n,cmp);
	
	f[0][0] = 1;
	for(int i = 1;i <= n;++ i){
		for(int j = 0;j <= min(i,k);++ j){
			f[i][j] = f[i-1][j];
			if(j)
				f[i][j] = max(f[i][j],f[i-1][j-1]*x[i].a+x[i].b);
		}
	}
	
	cout << f[n][k];
	
	return 0;
	
}
posted @ 2024-08-10 21:48  校牌杀手  阅读(37)  评论(0编辑  收藏  举报