title :2022牛客暑期多校训练营2 题解
date : 2022-8-16
tags : ACM,练习记录
author : Linno
题目链接 :https://ac.nowcoder.com/acm/contest/33186
补题进度 :9/11
设
f
k
[
i
]
[
j
]
表示长度为
k
的从
i
开始到
j
结束的子序列最大长度,状态转移方程为
:
f
k
[
i
]
[
j
]
=
{
d
i
s
(
i
,
j
)
,
k
=
2
m
a
x
t
{
f
k
−
1
[
i
]
[
t
]
+
f
2
[
t
]
[
j
]
}
,
k
≥
3
设f_k[i][j]表示长度为k的从i开始到j结束的子序列最大长度,状态转移方程为:\\ f_k[i][j]=
带有{max,+}运算的dp转移可以用矩阵快速幂优化,然后注意到关于决策点t具有决策单调性,可以直接将复杂度降为 O ( n 2 l o g k ) O(n^2logk) O(n2logk)
#include
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N=2007;
const int mod=1e9+7;
double x[N],y[N];
struct Matrix{
int n,m;
vector<vector<double>>a;
Matrix(){}
Matrix(int n,int m):n(n),m(m){a.resize(n,vector<double>(m,0));}
Matrix operator *(const Matrix &s)const{
Matrix ans=Matrix(n,s.m);
vector<vector<int> >p(n,vector<int>(m,0));
for(int i=0;i<n;++i) p[i][i]=i;
for(int len=1;len<=n;++len){
for(int i=0;i+len<n;++i){
for(int j=i+len,k=j?p[i][j-1]:0;k<=p[i+1][j];++k){
if(ans.a[i][j]<a[i][k]+s.a[k][j]){
ans.a[i][j]=a[i][k]+s.a[k][j];
p[i][j]=k;
}
}
}
}
return ans;
}
};
Matrix fpow(Matrix a,ll b){
Matrix ans=Matrix(a.n,a.m);
while(b){
if(b&1) ans=ans*a;
a=a*a;
b>>=1;
}
return ans;
}
inline double dis(int i,int j){
return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
void Solve(){
int n,k;
scanf("%d%d",&n,&k);
for(int i=0;i<n;++i) scanf("%lf%lf",x+i,y+i);
Matrix bas=Matrix(n,n);
for(int i=0;i<n;++i){
for(int j=i+1;j<n;++j){
bas.a[i][j]=dis(i,j);
}
}
Matrix ans=fpow(bas,k-1);
double mx=0;
for(int i=0;i<n;++i){
for(int j=i+1;j<n;++j) mx=max(mx,ans.a[i][j]+dis(i,j));
}
printf("%.10lf\n",mx);
}
signed main(){
int T=1;
while(T--){
Solve();
}
return 0;
}
一般Nim游戏结论:异或和为0必败,否则必胜。那么先手把必败态转移为必胜态,只需要将异或和为1的数位变成另即可。①考虑必败的情况下,我们要尽量延长回合数,实际上任取一个即可,因为对手必须把最低位异或和变为0,因此也只能减一个。特殊地,初始操作时如果有某一堆存在两个连续的1会使得使得对手可以取多个石子,emofunc哥哥有比较详细的证明,用一个函数check一下即可。考虑必胜的情况下,对方会让我们进入只能取一个的局面,所以就是问我们第一步最多能取多少个,有多少种取法,因此记录最高的lowbit(a[i])使得s^lowbit(a[i])
一个很经典的二分+Spfa,比赛时我忘记了有取对数变成加法来进行松弛的操作wa了一发,然后我把松弛的乘法换成了除法精度就够了。(不知道能不能被卡掉) 设
G
n
,
k
G_{n,k}
Gn,k表示长度为n的字符串至少有k个bit子串,方案数为
G
n
,
k
=
C
n
−
2
k
k
∗
2
6
n
−
3
k
G_{n,k}=C_{n-2k}^k*26^{n-3k}
Gn,k=Cn−2kk∗26n−3k,利用容斥推除长度为n的字符恰好有k个bit子串方案数为
F
n
,
k
=
∑
j
≥
k
(
−
1
)
j
−
k
C
j
k
F_{n,k}=\sum_{j\ge k}(-1)^{j-k}C_j^k
Fn,k=∑j≥k(−1)j−kCjk。经典地多项式卷积,可化为
k
!
F
n
,
k
=
∑
j
≥
k
H
n
,
n
−
j
+
k
j
!
G
n
,
j
k!F_{n,k}=\sum_{j\ge k}H_{n,n-j+k}j!G_{n,j}
k!Fn,k=∑j≥kHn,n−j+kj!Gn,j,套一个NTT卷积即可。 感觉好复杂了没写。 很直观地能看出上升的段最多不超过根号级别,根据这个感觉把数列设成阶梯状就行了。 把上电梯和下电梯的人分成两堆,每轮贪心地去选人并且送到当轮的最高点,用两个堆来维护即可。 如果求两两点对之间的效率则是
O
(
n
2
)
O(n^2)
O(n2)的复杂度,考虑分别构建三个大小分别为(n,k),(k,n)和(n,d)的矩阵,其效果为相乘等价于求
∑
i
−
1
n
x
i
⋅
x
j
∣
x
i
∣
∣
x
j
∣
\sum_{i-1}^n\frac{x_i·x_j}{|x_i||x_j|}
∑i−1n∣xi∣∣xj∣xi⋅xj,就可以用
O
(
2
k
n
d
)
O(2knd)
O(2knd)的复杂度求解。重点:利用结合律尽量减少矩阵运算。 高中知识,最小二乘法。 区间dp,设
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k]表示最终补成长度为i的字符串,已经匹配了a串中的的j个,并且剩余未匹配的左括号数为k时的方案数,那么分别枚举i,j,k,并且判断str[j]是左括号还是右括号分别进行转移即可。 又是一个dp,
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示在第i个师姐中到达j点,最晚可以从哪个世界出发,转移实际上就是和前面的世界取一个max,那么枚举第i个世界时,答案就是i-f[m]+1,这题卡空间,然后我们滚动掉第一维即可。#includeD - Link with Game Glitch
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#includeE - Falfa with Substring
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#includeF - NIO with String Game
G - Link with Monotonic Subsequence
#include H - Take the Elevator
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#includeI - let fat tension
#includeJ - Link with Arithmetic Progression
#include K - Link with Bracket Sequence I
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#includeL - Link with Level Editor I
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include