前言
由于时间过少,这里仅仅记录我自己的思路(给自己看的),如果你有兴趣可以看看原题,再看看我写的,但是一般情况下不会很友好,此类文章在以后都会标记“(记录型博客)”
A
我们发现,所有的剩余量一定是
a
+
2
b
a+\sqrt 2 b
a+2b或者是
C
−
(
a
+
2
b
)
C-(a+\sqrt 2 b)
C−(a+2b)的形式
把所有这些形式的全部抽出来
分析一下种类好像不是很多,然后建图跑最短路即可
B
首先有个性质,就是这么生成的树的直径一定是小于等于
O
(
l
o
g
n
)
\mathcal O(logn)
O(logn)的
然后考虑dp
设
f
u
,
l
f_{u,l}
fu,l表示以
u
u
u为根,直径不超过
d
d
d,距离
u
u
u的最远点距离不超过l的子树最多包含的点数
转移是对于每个
f
u
,
l
f_{u,l}
fu,l,加入儿子,枚举儿子更新到的
l
l
l,我们发现只需要
l
+
s
+
1
≤
d
l+s+1\le d
l+s+1≤d即可,所以是一个前缀max形式,对于
s
+
1
>
l
s+1>l
s+1>l的情况另外处理一遍即可
然后,在枚举删去点的数量的时候,我们只需要枚举剩余直径长度即可,所以要扫
O
(
l
o
g
n
)
\mathcal O(logn)
O(logn)遍,每遍复杂度
O
(
n
l
o
g
n
)
\mathcal O(nlogn)
O(nlogn)
综上总复杂度
O
(
n
l
o
g
2
n
)
\mathcal O(nlog^2n)
O(nlog2n)
C
排序后直接统计相邻两个数相差大于m的数量即可
D
感觉是构造题?
n
n
n是奇数的话
1
2
3
⋅
⋅
⋅
n
−
1
n
n
1
2
⋅
⋅
⋅
n
−
2
n
−
1
n
−
1
n
1
⋅
⋅
⋅
n
−
3
n
−
2
⋅
⋅
⋅
⋅
⋅
⋅
⋅
⋅
⋅
⋅
⋅
⋅
⋅
⋅
⋅
⋅
⋅
⋅
2
3
4
⋅
⋅
⋅
n
1
\begin{matrix} 1&2&3&···&n-1&n\\ n&1&2&···&n-2&n-1\\ n-1&n&1&···&n-3&n-2\\ ···&···&···&···&···&···\\ 2&3&4&···&n&1 \end{matrix}
1nn−1⋅⋅⋅221n⋅⋅⋅3321⋅⋅⋅4⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅n−1n−2n−3⋅⋅⋅nnn−1n−2⋅⋅⋅1
就好了
然后若
n
=
4
n=4
n=4
1
2
3
4
4
3
2
1
2
1
4
3
3
4
1
2
\begin{matrix} 1&2&3&4\\ 4&3&2&1\\ 2&1&4&3\\ 3&4&1&2\\ \end{matrix}
1423231432414132
之后就构造了
设已经求出答案为
n
n
n的矩阵
A
(
n
)
A(n)
A(n)
沿着对角线翻转的矩阵为
A
′
(
n
)
A'(n)
A′(n)
现在要对新的
n
n
n求答案,设
n
=
2
k
n=2k
n=2k
A
(
2
k
)
=
{
A
(
k
)
+
k
A
′
(
k
)
A
(
k
)
%
k
+
1
A
(
k
)
+
k
}
A(2k)=\left\{ \begin{matrix} A(k)+k&A'(k)\\ A(k)\%k+1&A(k)+k \end{matrix}\right\}
A(2k)={A(k)+kA(k)%k+1A′(k)A(k)+k}
由于
A
(
k
)
+
k
A(k)+k
A(k)+k本身不会不满足对称的性质
考虑
A
(
k
)
%
k
+
1
A(k)\%k+1
A(k)%k+1和
A
′
(
k
)
A'(k)
A′(k),原本
A
(
k
)
A(k)
A(k)和
A
′
(
k
)
A'(k)
A′(k)是完全对称的,现在
A
(
k
)
A(k)
A(k)全部改了值,所以问题就解决了
E
建出所有免费走的地方之间的边,跑最短路即可
复杂度
O
(
n
2
l
o
g
2
n
)
\mathcal O(n^2log^2n)
O(n2log2n)
F
按照权值从小到大加边
新增点权为某个节点到当前点路径的xor
然后建trie,加边合并的时候启发式在trie里查询
总复杂度
O
(
n
l
o
g
2
n
)
\mathcal O(nlog^2n)
O(nlog2n)
G
如果没有最后的交换操作的话直接贪心取段,然后将最多的段数与k比较即可
然后考虑有交换的情况,这就非常麻烦了
我们考虑
[
a
1
,
a
2
]
[
b
1
,
b
2
]
[
c
1
,
c
2
]
[
d
1
,
d
2
]
[
e
1
,
e
2
]
[
f
1
,
f
2
]
⋅
⋅
⋅
[a_1,a_2][b_1,b_2][c_1,c_2][d_1,d_2][e_1,e_2][f_1,f_2]···
[a1,a2][b1,b2][c1,c2][d1,d2][e1,e2][f1,f2]⋅⋅⋅的情况
假设我们要交换两段,我们发现左段的左端点一定是某段的左端点,右段的右端点一定是某段的右端点(否则一定能分开新的段)
假设我们现在左段左端在
x
1
x_1
x1,右段右端在
y
2
y_2
y2
我们发现若
x
≠
y
x\neq y
x=y则交换之后一定不成立
所以
x
=
y
x=y
x=y
于是我们相当于要把一段内部花一次交换的代价以分成更多段
我们发现只要确定第一段和最后一段的端点,中间的当成不能交换的问题做就好了
我们要把第一段和最后一段翻转,所以第一段一定要包含R,最后一段一定要包含L,然后我们通过贪心把所有可以作为第一段的右端点的点都搞出来,我们发现这些点也可以成为最后一段的左端点,考虑这些分的段,如果除了第一段与最后一段剩下大于1段的,这两段排序后将不有序,所以一定只有贪心出来的一段能继续再分,于是我们就将每一段求出最大值取max即可
这里的段的示意图
[
x
k
,
n
]
[
x
k
−
1
,
x
k
)
⋅
⋅
⋅
[
x
1
,
x
2
)
[
1
,
x
1
)
[x_k,n][x_{k-1},x_k)···[x_1,x_2)[1,x_1)
[xk,n][xk−1,xk)⋅⋅⋅[x1,x2)[1,x1),我们每次算一个
[
x
i
,
x
i
+
1
]
[x_i,x_{i+1}]
[xi,xi+1]的答案就好了
,总复杂度
O
(
n
)
\mathcal O(n)
O(n)
下面是AC代码
题目链接
#include<cstdio>
#include<cctype>
#include<algorithm>
//#include<ctime>
#define rg register
typedef long long ll;
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline void mind(T&a,const T b){a=a<b?a:b;}
template <typename T> inline void maxd(T&a,const T b){a=a>b?a:b;}
template <typename T> inline T abs(const T a){return a>0?a:-a;}
template <typename T> inline void Swap(T&a,T&b){T c=a;a=b;b=c;}
//template <typename T> inline void swap(T*a,T*b){T c=a;a=b;b=c;}
template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);}
template <typename T> inline T lcm(const T a,const T b){return a/gcd(a,b)*b;}
template <typename T> inline T square(const T x){return x*x;};
template <typename T> inline void read(T&x)
{
char cu=getchar();x=0;bool fla=0;
while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}
while(isdigit(cu))x=x*10+cu-'0',cu=getchar();
if(fla)x=-x;
}
template <typename T> inline void printe(const T x)
{
if(x>=10)printe(x/10);
putchar(x%10+'0');
}
template <typename T> inline void print(const T x)
{
if(x<0)putchar('-'),printe(-x);
else printe(x);
}
int n,k,a[1000001],maxplace,las=1;
int ok[1000001],tot;
void rt(const bool fla)
{
if(fla)puts("Yes");
else puts("Poor Simon");
exit(0);
}
int le,aaa[1000001];
int calc2()
{
int ma=0,an=0;
for(int i=1;i<=le;i++)
{
maxd(ma,aaa[i]);
if(i==ma)an++;
}
return an;
}
int len,A[1000001];
int calc1()
{
maxplace=0;
int ans=0,fst=0,la=1;
for(int i=1;i<=len;i++)
{
maxd(maxplace,A[i]);
if(i==maxplace)
{
fst++;
if(fst!=1&&i!=len)
{
le=i-la+1;
for(int j=1;j<=le;j++)aaa[le-j+1]=A[la+j-1]-la+1;
maxd(ans,calc2());
}
la=i+1;
}
}
return ans+min(fst,2);
}
int check(int l,int r)
{
len=r-l+1;
for(int i=1;i<=len;i++)A[len-i+1]=a[l+i-1]-l+1;
return calc1()-1;
}
int main()
{
read(n),read(k);
for(int i=1;i<=n;i++)read(a[i]);
for(int i=1;i<=n;i++)
{
maxd(maxplace,a[i]);
if(i==maxplace)ok[i]=1,tot++;
}
if(tot>=k)rt(1);
for(int i=1;i<=n;i++)
if(ok[i])
{
if(tot+check(las,i)>=k)rt(1);
las=i+1;
}
rt(0);
return 0;
}
H
我们发现只要赢的次数最多,直接贪心即可,两行代码足矣
I
大概是经典的FFT题目,先转化为原根求和,变成加法问题后直接用FFT做卷积即可
J
我们发现,我们要求的是最长不上升子序列即
0
⋅
⋅
⋅
01
⋅
⋅
⋅
1
0···01···1
0⋅⋅⋅01⋅⋅⋅1形式
要求所有的
(
s
u
m
0
+
s
u
m
1
)
s
u
m
0
=
s
u
m
0
2
+
s
u
m
1
s
u
m
0
(sum_0+sum_1)sum_0=sum_0^2+sum_1sum_0
(sum0+sum1)sum0=sum02+sum1sum0
考虑枚举最右边的一个
0
0
0
我们要求的是
∑
s
u
m
1
\sum sum_1
∑sum1,并且要满足这是最右边的
0
0
0,如果右边有更多就不优
考虑dp,
f
i
,
j
,
g
i
,
j
f_{i,j},g_{i,j}
fi,j,gi,j分别表示到第i个数,1的数量,方案数推完即可,复杂度
O
(
n
2
)
\mathcal O(n^2)
O(n2)(容易发现
1
1
1的数量要一直大于
0
0
0的数量)
再考虑求左侧的情况,要求
∑
s
u
m
0
\sum sum_0
∑sum0非常方便,同理即可(不过这里容易发现
0
0
0的数量要一直大于等于
1
1
1的数量)
然后求
∑
s
u
m
0
2
\sum sum_0^2
∑sum02好像也没啥区别,贡献的时候有个平方就好了
然后就做完了
复杂度
O
(
n
3
)
\mathcal O(n^3)
O(n3)
K
容易发现,对于原序列,每一个左括号都有相应的有括号进行匹配,对于任何截取的一段
l
,
r
l,r
l,r,括号匹配的要求是不变的,所以直接维护每个左括号匹配的右括号位置即可,每次询问用一个区间max即可
)厉害的人可以搞一个
O
(
n
)
−
O
(
1
)
\mathcal O(n)-\mathcal O(1)
O(n)−O(1)rmq
L
根据扩展欧几里得可知x,y的通解
现在相当于求
a
x
(
b
+
x
)
+
c
y
(
d
+
y
)
ax(b+x)+cy(d+y)
ax(b+x)+cy(d+y)
然后因为给出的系数都很小,好像枚举就好了
结语
完