2020.11.11随笔

好像博客好长时间没有进行更新了,也很长时间没有进行过总结了,来随便写几道最近印象比较深刻的题目吧。

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 (ai1+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 k1这个区间以及 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][k1]+dp[k+1][j]+calc(a[i1],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] [2kti+1,2kti+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] [tti+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 aipi 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(clogn) c c c为达到65536的个数。这样找到对应点后就可以直接暴力修改其 h a s h hash hash值。由于每次的变换量为1,所以最后每个点的修改次数不会很多。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值