前言
第一次冲到
r
k
4
(
o
f
f
i
c
i
a
l
)
rk4(official)
rk4(official),但是
C
,
E
C,E
C,E两道套路题做得还是太慢.
A A A
冒泡排序是否能在 n ( n − 1 ) 2 − 1 \dfrac{n(n-1)}{2}-1 2n(n−1)−1次内完成.
n ≤ 1 e 5 n\le 1e5 n≤1e5.
显然至多 n ( n − 1 ) 2 \dfrac{n(n-1)}{2} 2n(n−1)次,这种情况当且仅当序列为严格下降序列.
void solve() {
qr(n);
bool flag=1;
for(int i=1;i<=n;i++) qr(a[i]);
for(int i=2;i<=n;i++) if(a[i-1]<=a[i]) flag=0;
if(!flag) puts("YES");
else puts("NO");
}
B B B
给定一个数组 a ( 1 ≤ a i ≤ 1 0 9 ) a(1\le a_i\le 10^9) a(1≤ai≤109).
求有序数对 ( i < j ) (i<j) (i<j)满足 a i & a j ≥ a i ⊕ a j a_i\&a_j\ge a_i\oplus a_j ai&aj≥ai⊕aj的数量.
n ≤ 1 e 5 n\le 1e5 n≤1e5.
一眼可以猜到用最高位来作为判定基准.
如果最高位相同,
&
\&
&得到最高位的1,
⊕
\oplus
⊕没有最高位的1,显然满足.
反之,一定不满足.
如果题目允许 a i = 0 a_i=0 ai=0,那么 0 0 0只能和 0 0 0形成配对,所以我们记一下 0 0 0的个数照样可做.
int n,a[N],c[33];
void solve() {
qr(n);
memset(c,0,sizeof c); ll ans=0;
for(int i=1,x;i<=n;i++) {
qr(x);
for(int j=30;j>=0;j--)
if(x>>j&1) {c[j]++; break;}
}
for(int j=30;j>=0;j--) ans += (ll)c[j]*(c[j]-1)/2;
pr2(ans);
}
C C C
给定一个 n n n的排列 a a a,求一个子序列的权值最大值.
设 1 ≤ b 1 < b 2 < . . . < b k ≤ n 1\le b_1<b_2<...<b_k\le n 1≤b1<b2<...<bk≤n表示子序列的对应下标,
我们定义权值为 a b 1 − a b 2 + a b 3 − a b 4 . . . a_{b_1}-a_{b_2}+a_{b_3}-a_{b_4}... ab1−ab2+ab3−ab4...(奇数位置为+,偶数位置为-).
有 q q q次修改操作,每一次交换两个位置 a l , a r a_l,a_r al,ar.
对于每个历史状态,输出子序列权值的最大值.
n , q ≤ 3 e 5 n,q\le 3e5 n,q≤3e5
看到数据范围,直接想到用线段树做
d
p
dp
dp.
我们只要记录奇偶长度的最大和最小权值即可.
转移如下:
struct rec {
ll l[2],r[2],res;//最小值,最大值,答案,l[0]/r[0]表示偶数长度的权值.
rec() {l[0]=r[0]=0; l[1]=r[1]=0; res=0;}
rec operator +(rec b) const {
rec c=*this;
c.res=max(c.res,b.res);
for(int i=0;i<2;i++)
c.l[i]=min(c.l[i],b.l[i]),
c.r[i]=max(c.r[i],b.r[i]);
for(int i=0;i<2;i++)
for(int j=0;j<2;j++) {
c.l[(i+j)&1]=min(c.l[(i+j)&1],l[i]+(i&1?-b.r[j]:b.l[j]));
c.r[(i+j)&1]=max(c.r[(i+j)&1],r[i]+(i&1?-b.l[j]:b.r[j]));
}
for(int i=0;i<2;i++) c.res=max(c.res,c.r[i]);
return c;
}
} f[N<<2];
转移的时候考虑符号即可.
int n,q,a[N];
struct rec {
ll l[2],r[2],res;
rec() {l[0]=r[0]=0; l[1]=r[1]=0; res=0;}
rec operator +(rec b) const {
rec c=*this;
c.res=max(c.res,b.res);
for(int i=0;i<2;i++)
c.l[i]=min(c.l[i],b.l[i]),
c.r[i]=max(c.r[i],b.r[i]);
for(int i=0;i<2;i++)
for(int j=0;j<2;j++) {
c.l[(i+j)&1]=min(c.l[(i+j)&1],l[i]+(i&1?-b.r[j]:b.l[j]));
c.r[(i+j)&1]=max(c.r[(i+j)&1],r[i]+(i&1?-b.l[j]:b.r[j]));
}
for(int i=0;i<2;i++) c.res=max(c.res,c.r[i]);
return c;
}
} f[N<<2];
void bt(int x,int l,int r) {
if(l == r) {
f[x]=rec();
f[x].l[1]=f[x].r[1]=f[x].res=a[l];
return ;
}
int mid=(l+r)/2;
bt(lc,l,mid);
bt(rc,mid+1,r);
f[x]=f[lc]+f[rc];
}
void change(int x,int l,int r,int pos) {
if(l == r) {
f[x]=rec();
f[x].l[1]=f[x].r[1]=f[x].res=a[l];
return;
}
int mid=(l+r)/2;
if(pos<=mid) change(lc,l,mid,pos);
else change(rc,mid+1,r,pos);
f[x]=f[lc]+f[rc];
}
void solve() {
qr(n); qr(q);
for(int i=1;i<=n;i++) qr(a[i]);
bt(1,1,n);
pr2(f[1].res);
while(q--) {
int l,r; qr(l); qr(r);
swap(a[l],a[r]);
change(1,1,n,l);
change(1,1,n,r);
pr2(f[1].res);
}
}
int main() {
int T;
qr(T);
while(T--)
solve();
}
l
o
c
k
lock
lock之后,到
r
o
o
m
room
room内看到神奇贪心做法…
显然,子序列的长度为奇数.
然后序列可以看作若干段极大下降序列的拼接.
我们对于一个下降子数组,在头加,在尾减一定最优.
这里的头定义为局部极大值,尾定义尾局部极小值.
显然序列的第一个头一定前于第一个尾,最后一个头一定在最后一个尾的后面.
特别的,令
a
0
=
a
n
+
1
=
0
a_0=a_{n+1}=0
a0=an+1=0.
int n,q,a[N];
ll ans;
void insert(int x,int d=1) {
if(1 <= x && x <= n) {
if(a[x-1]<a[x]&&a[x]>a[x+1]) ans += a[x]*d;
if(a[x-1]>a[x]&&a[x]<a[x+1]) ans -= a[x]*d;
}
}
void solve() {
qr(n); qr(q); ans=0; a[n+1]=a[0]=0;
for(int i=1;i<=n;i++) qr(a[i]);
for(int i=1;i<=n;i++) insert(i);
pr2(ans); while(q--) {
int l,r,i; qr(l); qr(r);
if(l == r) {pr2(ans); continue;}
for(i=l-1;i<=l+1;i++) insert(i,-1);
for(i=max(i,r-1);i<=r+1;i++) insert(i,-1);
swap(a[l],a[r]);
for(i=l-1;i<=l+1;i++) insert(i);
for(i=max(i,r-1);i<=r+1;i++) insert(i);
pr2(ans);
}
}
D D D
有 n n n盏灯,灯亮的时间为 [ l i , r i ] [l_i,r_i] [li,ri].
给定常数 k k k.问有多少种大小为 k k k的灯集合在某一时刻能够都亮.
计数题,我们要选好计数的位置.
显然,一个子集对应的所有线段的交的左端点唯一.
我们以这个左端点作为计数点.
我们容易用差分记录一个位置被多少盏灯覆盖.
设前面转移过来的灯有
a
a
a盏,当前位置有
b
b
b盏.
那么为了保证不重不漏我们必须保证至少选择
b
b
b盏之一.(左端点固定)
所以总方案为
C
a
+
b
k
−
C
a
k
C_{a+b}^k-C_a^k
Ca+bk−Cak.
int n,a,b,k;
map<pii,int> s;
ll ans,jc[N],inv[N];
ll C(int x,int y) {
return x<y?0:jc[x]*inv[y]%mod*inv[x-y]%mod;
}
void solve() {
qr(n); qr(k);
jc[0]=1; for(int i=1;i<=n;i++) jc[i]=jc[i-1]*i%mod;
inv[n]=power(jc[n]); for(int i=n;i;i--) inv[i-1]=inv[i]*i%mod;
for(int i=1,l,r;i<=n;i++) {
qr(l); qr(r);
s[mk(l,1)]++;
s[mk(r+1,-1)]++;
}
for(auto it:s) {
pii p=it.fi; b=it.se;
if(p.se == -1) a -= b;
else {
ans += C(a+b,k)-C(a,k);
a += b;
}
}
pr2((ans%mod+mod)%mod);
}
E E E
有 n n n只战斗旅鼠,其中有若干只持盾.
对于一个防御队形,我们定义保护值为数对 ( i , j ) (i,j) (i,j)[ i < j i<j i<j且 i , j i,j i,j不持盾但是中间有一个盾(即位于 ( i , j ) (i,j) (i,j))].
对于每一个移动步数 k ∈ [ 0 , n ∗ ( n − 1 ) 2 ] k\in [0,\dfrac {n*(n-1)}{2}] k∈[0,2n∗(n−1)],求移动次数至多 k k k次的最大保护值.
n ≤ 80 n\le 80 n≤80.
设总共有
k
k
k个盾,位置为
a
a
a:
a
1
<
a
2
<
a
3
<
.
.
.
<
a
k
a_1<a_2<a_3<...<a_k
a1<a2<a3<...<ak.
特别的,定义
a
0
=
0
,
a
k
+
1
=
n
+
1
a_0=0,a_{k+1}=n+1
a0=0,ak+1=n+1.
那么
a
n
s
=
C
n
−
k
2
−
∑
i
=
1
k
C
a
i
−
a
i
−
1
−
1
2
ans=C_{n-k}^2 -\sum_{i=1}^k C_{a_i-a_{i-1}-1}^2
ans=Cn−k2−∑i=1kCai−ai−1−12.
所以我们
d
p
dp
dp求后面部分的最小值即可.
状态参数:盾的数量,最后一个盾的位置,总移动步数.
状态意义:
∑
i
=
1
k
C
a
i
−
a
i
−
1
−
1
2
\sum_{i=1}^k C_{a_i-a_{i-1}-1}^2
∑i=1kCai−ai−1−12的最小值.
总复杂度为
O
(
n
5
)
O(n^5)
O(n5)(可以轻松通过此题)
int n,a[N],tot,f[N][N][N*N/2];
void upd(int &x,int y) {x=max(x,y);}
void solve() {
qr(n); int m=n*(n-1)/2;
for(int i=1,x;i<=n;i++) {
qr(x);
if(x) a[++tot]=i;
}
int ans=(n-tot)*(n-tot-1)/2;
memset(f,-2,sizeof f); int init=f[0][0][0]; f[0][0][0]=0;
a[++tot]=n+1;
for(int i=0;i<tot;i++)
for(int j=i;j<=n;j++)
for(int x=j+1;x<=n+1;x++) {
int d=abs(a[i+1]-x),val=(x-j-1)*(x-j-2)/2;
for(int k=0;k<=m;k++) if(f[i][j][k] > init)
upd(f[i+1][x][k+d],f[i][j][k]-val);
}
for(int i=0;i<=m;i++)
pr1(ans+f[tot][n+1][i]),upd(f[tot][n+1][i+1],f[tot][n+1][i]);
}