#include<iostream>#include<algorithm>#include<cstring>usingnamespace std;intgcd(int a,int b){if(b ==0)return a;returngcd(b, a % b);}int a[10], b[10];intmain(){for(int i =1; i <=6; i++)scanf("%d",&a[i]);for(int i =1; i <=6; i++)scanf("%d",&b[i]);int res =0;for(int i =1; i <=6; i++){for(int j =1; j <=6; j++){
res += a[i]> b[j];}}int d =gcd(res,36);
res /= d;printf("%d/%d\n", res,36/ d);return0;}
我们这样思考,假设第一个位置 a[1] 就是等差序列的起点,那么我们可以计算每一个点的移动,记录最多向左移动的距离 lmost,和最多向右移动的距离 rmost。如果我们要把等差序列起点左移的话,rmost一定会增加。如果右移的话,lmost 必然会增加。因为我们只关心最大值。那么,我们可知最小化的最大值就是
l
m
o
s
t
+
r
m
o
s
t
2
\frac{lmost + rmost}{2}
2lmost+rmost. 但是我们也要注意的是,可能是个递减序列,因此要把
D
∗
=
−
1
D *= -1
D∗=−1,然后再跑一遍上述过程。
然后,神奇的题目,用 GNU C++17 (64) 会莫名其妙超时,我查看了一下,答案已经输出了,但是似乎检测出了问题?评测姬快来背锅
#include<iostream>#include<algorithm>#include<cstring>usingnamespace std;constint maxn =1000010;typedeflonglong ll;
ll a[maxn], D;int N;
ll f(){
ll lmost =0, rmost =0;for(int i =1; i <= N; i++){
ll x = a[1]+(i -1LL)* D;if(x > a[i]) rmost =max(rmost, x - a[i]);if(x < a[i]) lmost =max(lmost, a[i]- x);}return(lmost + rmost)/2;}intmain(){scanf("%d%lld",&N,&D);
D *=10;for(int i =1; i <= N; i++){scanf("%lld",&a[i]);
a[i]*=10;}
ll ans =f();
D =-D;
ans =min(ans,f());printf("%lld.%lld\n", ans /10, ans %10);return0;}
我们想想充要条件是什么。从第一条直线和第三条直线上分别任取一点
x
1
,
x
3
x_1, x_3
x1,x3. 那么,如果第二条直线上有一点可以和
x
1
,
x
3
x_1, x_3
x1,x3 共线,等价于
x
2
=
x
1
+
x
3
2
x_2 = \frac{x1 + x3}{2}
x2=2x1+x3. 因此,我们看看第一条直线和第三条直线可以有多少种组合形式。那么,是不是可以看成两个多项式的卷积啊。设
f
=
a
∗
c
f = a * c
f=a∗c,然后答案就是
∑
b
[
i
]
∗
f
[
2
∗
i
]
\sum\limits b[i]* f[2*i]
∑b[i]∗f[2∗i].
不过傅里叶变换一定要小心细节。(1)数组中存的是多项式的系数。(2)最后a.x一定要除以 tot 才是卷积的系数。(3)数组开多大取决于数的取值范围,而且一定要大于2的整数幂。
由此可见,计数类问题,有时候可以用FFT解决。因为计数类问题有时候可以转化为多项式得卷积。
#include<iostream>#include<algorithm>#include<cstring>#include<cmath>usingnamespace std;constdouble PI =acos(-1);constint maxn =300010;typedeflonglong ll;struct Complex {double x, y;
Complex operator+(const Complex& t)const{return{ x + t.x, y + t.y };}
Complex operator-(const Complex& t)const{return{ x - t.x, y - t.y };}
Complex operator*(const Complex& t)const{return{ x * t.x - y * t.y, x * t.y + y * t.x };}}a[maxn], c[maxn];int rev[maxn], bit, tot;int b[maxn];voidfft(Complex a[],int inv){for(int i =0; i < tot; i++){if(i < rev[i])swap(a[i], a[rev[i]]);}for(int mid =1; mid < tot; mid <<=1){auto w1 =Complex({cos(PI / mid), inv *sin(PI / mid)});for(int i =0; i < tot; i += mid *2){auto wk =Complex({1,0});for(int j =0; j < mid; j++, wk = wk * w1){auto x = a[i + j], y = wk * a[i + j + mid];
a[i + j]= x + y, a[i + j + mid]= x - y;}}}}int dif =30000;intmain(){int N1, N2, N3;scanf("%d",&N1);for(int i =1; i <= N1; i++){int x;scanf("%d",&x);
a[x + dif].x +=1;}scanf("%d",&N2);for(int i =1; i <= N2; i++){int x;scanf("%d",&x);
b[x + dif]+=1;}scanf("%d",&N3);for(int i =1; i <= N3; i++){int x;scanf("%d",&x);
c[x + dif].x +=1;}
bit =17;
tot =(1<< bit);for(int i =0; i < tot; i++){
rev[i]=(rev[i >>1]>>1)|((i &1)<<(bit -1));}fft(a,1),fft(c,1);for(int i =0; i < tot; i++) a[i]= a[i]* c[i];fft(a,-1);
ll ans =0;//一定要小心,这里是数据范围最大,即60000//而且,卷积的结果在 a 中,要除以 tot 才对。而且,四舍五入的话(比如round),不要加上 0.5,+0.5意味着舍弃小数部分。for(int i =0; i <=60000; i++){
ll x = a[2* i].x / tot +0.5;
ans += x * b[i];}printf("%lld\n", ans);return0;}
I. Stock Analysis
给一个序列
(
n
≤
2000
)
(n\le 2000)
(n≤2000),给
Q
(
Q
≤
20000
)
Q(Q\le20000)
Q(Q≤20000) 组询问
(
l
,
r
,
u
)
(l, r, u)
(l,r,u),问在
(
l
,
r
)
(l, r)
(l,r) 内,不大于
u
u
u 的最大连续子段和是多少?
#include<iostream>#include<cstring>#include<algorithm>#include<vector>usingnamespace std;typedeflonglong ll;const ll INF =1e18;constint maxn =2010, maxm =200010;struct node {int l, r, id;
ll val;};
vector<node> q;
ll w[maxn], tr[maxn][maxn], ans[maxm];intlowbit(int x){return x &-x;}boolcmp(const node& u,const node& v){return u.val < v.val || u.val == v.val && u.id < v.id;}int N, M;voidupdate(int x,int y, ll v){while(x){int z = y;while(z <= N){
tr[x][z]=max(tr[x][z], v);
z += z &-z;}
x -= x &-x;}}
ll query(int x,int y){
ll res =-INF;while(x <= N){int z = y;while(z){
res =max(res, tr[x][z]);
z -= z &-z;}
x += x &-x;}return res;}intmain(){scanf("%d%d",&N,&M);for(int i =1; i <= N; i++){fill(tr[i], tr[i]+ N +1,-INF);}for(int i =1; i <= N; i++){scanf("%lld",&w[i]);
w[i]+= w[i -1];}for(int i =1; i <= N; i++){for(int j = i; j <= N; j++){
q.push_back({ i, j,0, w[j]- w[i -1]});}}for(int i =1; i <= M; i++){int l, r;
ll x;scanf("%d%d%lld",&l,&r,&x);
q.push_back({ l, r, i, x });}sort(q.begin(), q.end(), cmp);for(auto p : q){int l = p.l, r = p.r, id = p.id;
ll val = p.val;if(!id)update(l, r, val);else ans[id]=query(l, r);}for(int i =1; i <= M; i++){if(ans[i]!=-INF)printf("%lld\n", ans[i]);elseprintf("NONE\n");}return0;}
J. Switches
题意:已知有
n
n
n 个开关和
n
n
n 盏灯,现在每一个开关可以控制若干盏灯,该信息用矩阵表示。一盏灯要亮,当且仅当这盏灯对应的开关数量为奇数。问对于每一盏灯,能否打开若干个开关,使得只有该盏灯是亮的,而其他灯都是灭的。若可以,输出每一盏灯对应的开关,否则输出
−
1
-1
−1。
注意题目输入,说的是第 i 行表示第 i 个开关控制那些灯。而我们在列异或方程组的时候,每一行应该是都有哪些开关控制着第 i 个灯。因此要把输入矩阵转置一下。
#include<iostream>#include<cstring>#include<algorithm>#include<bitset>usingnamespace std;constint maxn =510;
bitset<maxn *2> a[maxn];
bitset<maxn> b, inv_a[maxn], ans;int N;boolGauss_inv(){for(int c =1; c <= N; c++){int t = c;for(int i = c; i <= N; i++){if(a[i][c]){
t = i;break;}}if(a[t][c]==0)returnfalse;swap(a[t], a[c]);//小心这个地方要从第一行开始看。大雪菜那个是从 r + 1 行开始,那是因为他没有化成单位矩阵//但是求逆矩阵必须要化为单位阵。for(int i =1; i <= N; i++){if(a[i][c]&& i != c){
a[i]^= a[c];}}}return1;}// A = B * Cvoidmul(bitset<maxn>& a, bitset<maxn> b[], bitset<maxn>& c){for(int i =1; i <= N; i++){//小心这个地方是与,不是异或。矩阵乘法怎么可以写异或呢!
a[i]=(b[i]& c).count()%2;}}intmain(){scanf("%d",&N);for(int i =1; i <= N; i++){
a[i][i + N]=1;for(int j =1; j <= N; j++){int x;scanf("%d",&x);if(x) a[j][i]=1;}}if(!Gauss_inv()){printf("-1\n");}else{for(int i =1; i <= N; i++){for(int j =1; j <= N; j++) inv_a[i][j]= a[i][j + N];}/*for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) cout << a[i][j + N] << " ";
cout << endl;
}*/for(int i =1; i <= N; i++){
b.reset();
b[i]=1;
ans.reset();mul(ans, inv_a, b);for(int i =1; i <= N; i++){if(ans[i])printf("%d ", i);}printf("\n");}}return0;}
K. Tiling Polyomino
挖坑。找到题解或者看懂代码再补
L. Two Buildings
题意:给定长度为
n
n
n 的数组
h
h
h,要求计算
m
a
x
{
(
h
[
i
]
+
h
[
j
]
)
∗
(
j
−
i
)
}
max\{(h[i]+h[j])*(j-i)\}
max{(h[i]+h[j])∗(j−i)},其中
i
<
j
i<j
i<j。数据范围:
n
<
=
1
e
6
,
1
<
=
h
(
i
)
<
=
1
e
6
n<=1e6,1<=h(i)<=1e6
n<=1e6,1<=h(i)<=1e6.
#include<iostream>#include<cstring>#include<algorithm>#define x first#define y secondusingnamespace std;constint maxn =1000010;int N, M;typedeflonglong ll;typedef pair<ll, ll> P;
P a[maxn], b[maxn];bool del[maxn];
ll cal(int j,int i){return(a[j].y - b[i].y)*(a[j].x - b[i].x);}
ll solve(int l1,int r1,int l2,int r2){if(l1 > r1 || l2 > r2)return1e-18;int mid =(l1 + r1)/2, pos = l2;
ll res =cal(mid, pos);for(int i = l2 +1; i <= r2; i++){
ll tmp =cal(mid, i);if(tmp >= res){
pos = i;
res = tmp;}}returnmax(res,max(solve(l1, mid -1, l2, pos),solve(mid +1, r1, pos, r2)));}intmain(){scanf("%d",&N);
M = N;for(int i =1; i <= N; i++){scanf("%lld",&a[i].y);
a[i].x = i;
b[i]={ i,-a[i].y };}sort(a +1, a + N +1);sort(b +1, b + N +1);int cnt =0, last = N;//删掉 a 中多余的点,越靠近右上方越优。for(int i = N -1; i >=1; i--){if(a[i].y <= a[last].y){
del[i]=true;}else{
last = i;}}for(int i =1; i <= N; i++){if(!del[i]){//注意,这个地方不可以写成 ++cnt, a[cnt] = {cnt, a[i].y},因为原本的位置信息必须要保留。
a[++cnt]= a[i];}}
N = cnt;//删掉 b 中左下方的点。memset(del,false,sizeof del);
last =1, cnt =0;for(int i =2; i <= M; i++){if(b[i].y >= b[last].y){
del[i]=true;}else{
last = i;}}for(int i =1; i <= M; i++){if(!del[i]){
b[++cnt]= b[i];}}
M = cnt;printf("%lld\n",solve(1, N,1, M));return0;}
比赛题目训练系列05 (2020-2021 ACM-ICPC, Asia Seoul Regional Contest)
比赛题目训练系列04 (2020-2021 ACM-ICPC, Asia Seoul Regional Contest)训练网址E. Imprecise ComputerG. Mobile RobotH. NeedleI. Stock AnalysisJ. SwitchesK. Tiling PolyominoL. Two Buildings