好像博客好长时间没有进行更新了,也很长时间没有进行过总结了,来随便写几道最近印象比较深刻的题目吧。
2020icpc网络赛的某题
题意:给定一个序列
{
a
n
}
\{a_n\}
{an},要消去
a
i
a_i
ai的代价是
(
a
i
−
1
+
a
i
+
a
i
+
1
)
2
(a_{i-1}+a_i+a_{i+1})^2
(ai−1+ai+ai+1)2,消去后序列重新变换一下下标。求消到最后两个元素的最小代价。
(
n
<
=
100
)
(n<=100)
(n<=100)
个人思考:对于这道题,有两种思考方向,一个是直接思考,另一个是逆向思考。直接思考就是一个一个消去,逆向思考则是一个一个加入。最开始以为这道题是个贪心,但是无论怎么去贪心,都感觉思考并不完整。当然,dp一直是这种问题的最优解,不过最开始就被带入了逆向思考的大坑,怎么去dp都觉得有问题。
题解:从正向去考虑,令
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]代表
i
i
i到
j
j
j这段区间全部都消去的最小费用。这里对区间的长度从小到大进行枚举,如果现在在计算
i
i
i到
j
j
j这个区间,那么假设这次是消去
k
k
k这个点
(
i
<
=
k
<
=
j
)
(i<=k<=j)
(i<=k<=j),消去
k
k
k后,
i
i
i到
j
j
j就全部消除了,那么
i
i
i到
k
−
1
k-1
k−1这个区间以及
k
+
1
k+1
k+1到
j
j
j都已经全部消除了,这样就可以转移了。
d
p
[
i
]
[
j
]
=
m
i
n
{
d
p
[
i
]
[
k
−
1
]
+
d
p
[
k
+
1
]
[
j
]
+
c
a
l
c
(
a
[
i
−
1
]
,
a
[
k
]
,
a
[
j
+
1
]
)
}
dp[i][j]=min\{dp[i][k-1]+dp[k+1][j]+calc(a[i-1],a[k],a[j+1]) \}
dp[i][j]=min{dp[i][k−1]+dp[k+1][j]+calc(a[i−1],a[k],a[j+1])}
2020ccpc绵阳站J题
题意:有n个灯,某个灯亮的时间为
[
2
k
∗
t
i
+
1
,
2
k
∗
t
i
+
t
i
]
[2k*t_i+1,2k*t_i+t_i]
[2k∗ti+1,2k∗ti+ti],暗的区间
[
(
2
k
+
1
)
∗
t
i
+
1
,
2
(
k
+
1
)
∗
t
i
]
[(2k+1)*t_i+1,2(k+1)*t_i]
[(2k+1)∗ti+1,2(k+1)∗ti],每个灯的亮度为
x
i
x_i
xi。求
[
1
,
m
]
[1,m]
[1,m]的时间内,每个时间点的最大亮度为多少。
(
n
,
m
<
=
1
e
5
,
t
i
<
=
1
e
5
)
(n,m<=1e5,t_i<=1e5)
(n,m<=1e5,ti<=1e5)
个人思考:最开始看到这题的时候,没什么很好的想法,感觉怎么做都像是暴力。后来lyk提了一个做法:对时间戳建立线段树,对每个时间,如果它是灯的切换瞬间,即 t = ( 2 k + 1 ) ∗ t i t=(2k+1)*t_i t=(2k+1)∗ti,那么拿它的亮度就更新前面一段时间 [ t − t i + 1 , t ] [t-t_i+1,t] [t−ti+1,t]。这样的复杂度理论上不高,每个点只要考虑它的奇数因子 x x x,然后看是否有灯的周期为 t / x t/x t/x。不过最后跑起来,还是 T T T掉了。(原因是自己没有预处理每个点的因数)
题解:这个题解是根据网上的题解理解的。考虑建立权值线段树,每个点表示周期为
[
l
,
r
]
[l,r]
[l,r]的结点的当前还亮着的最大亮度为多少。每次更新的时候,也是考虑它的因数,看是否存在为因数的周期的灯(怎么感觉复杂度和上面的做法差不多,事实证明的确一样)。最后,更新答案的时候,每次取根节点的权值就可以了。
附上训练赛的做法(个人思考所述)
#include<bits/stdc++.h>
#define For(aa,bb,cc) for(int aa=(bb);aa<=(int)(cc);++aa)
using namespace std;
const int maxn=2e5+10,N=2e5;
int n,m,k;
int G[maxn];
int ans[maxn];
vector<int>p[N];
void init(){
For(i,1,N) For(j,1,sqrt(i)) if(i%j==0){
if(j&1) p[i].push_back(j);
if((i/j)&1) p[i].push_back(i/j);
}
}
#define lr (node<<1)
#define rr (node<<1|1)
#define mid ((l+r)>>1)
int tree[maxn<<4],tag[maxn<<4];
inline void push_down(int node){
if(tag[node]){
tag[lr]=max(tag[lr],tag[node]);
tag[rr]=max(tag[rr],tag[node]);
tree[lr]=max(tree[lr],tag[node]);
tree[rr]=max(tree[rr],tag[node]);
tag[node]=0;
}
}
inline void push_up(int node){
tree[node]=max(tree[lr],tree[rr]);
}
void create(int node,int l,int r){
tree[node]=tag[node]=0;
if(l==r) return ;
create(lr,l,mid);
create(rr,mid+1,r);
}
void update(int node,int l,int r,int L,int R,int x){
push_down(node);
if(L==l && r==R){
tree[node]=max(tree[node],x);
tag[node]=max(tag[node],x);
return ;
}
if(R<=mid) update(lr,l,mid,L,R,x);
else if(L>mid) update(rr,mid+1,r,L,R,x);
else update(lr,l,mid,L,mid,x),update(rr,mid+1,r,mid+1,R,x);
push_up(node);
}
void query(int node,int l,int r){
if(l==r){
ans[l]=tree[node];
return ;
}
push_down(node);
query(lr,l,mid);
query(rr,mid+1,r);
push_up(node);
}
#undef lr
#undef rr
#undef mid
int main(){
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
init();
int _;
scanf("%d",&_);
For(__,1,_){
scanf("%d%d",&n,&m);
int t,x;
k=0;
memset(G,0,sizeof G);
For(i,1,n){
scanf("%d%d",&t,&x);
G[t]=max(G[t],x);
k=max(k,(m/t+1)*t);
}
create(1,1,k);
For(i,1,k){
For(j,0,p[i].size()-1){
t=i/p[i][j];
if(G[t]){
update(1,1,k,i-t+1,i,G[t]);
}
}
}
query(1,1,k);
printf("Case #%d:",__);
For(i,1,m) printf(" %d",ans[i]);puts("");
}
return 0;
}
2020ccpc威海站?题
题意:有一个数列
{
a
n
}
\{a_n\}
{an}。可以进行以下操作:
1.取
[
l
,
r
]
[l,r]
[l,r],让这个区间的
a
i
a_i
ai都加上1,然后对65536取模。
2.判断
[
l
1
,
r
1
]
[l_1,r_1]
[l1,r1]和
[
l
2
,
r
2
]
[l_2,r_2]
[l2,r2]是否相同。
个人思考:这道题是在考场上遇到的。对于这种类型的判断,我最开始是没什么想法的。不过有队友提出能不能用 h a s h hash hash来解决,我简单思考了一下,发现可以用 a i ∗ p i a_i*p^i ai∗pi来 h a s h hash hash,这样就可以直接通过区间 h a s h hash hash值来判断是否相等。但是在考场上没有想清楚怎么解决取模65536的问题。
题解:后面听到学长给出的一个做法,除了 h a s h hash hash的那个线段树外,另外再建立一颗线段树,专门维护 a i a_i ai的最大值,如果最大值达到65536,那么就直接把对应点给找出来,具体做法是进入最大值也为65536的子区间,这个复杂度是 O ( c ∗ l o g n ) O(c*log n) O(c∗logn), c c c为达到65536的个数。这样找到对应点后就可以直接暴力修改其 h a s h hash hash值。由于每次的变换量为1,所以最后每个点的修改次数不会很多。