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;
}