2021.11.13 B组总结

2 篇文章 0 订阅

2021.11.13 B组总结

赛时:

​ 九点多开始,一看题面,好家伙,线段树专题???

T1:

​ 逆序对,em ,105可以做,在开始30min左右,看出正解

T2:

​ 感觉是用字符串(回文串)相关知识,没感觉

T3:

​ 想到线段树,但是在实现出现问题,并且时间不足

T4:

​ 到最后,发现自己没看懂题,我蒻爆了

​ 最后只交了T1代码,思路正确,但是不开long long 见祖宗 60pts

​ 综上来看,

  1. 得分意识较低,没有认真对待每一场比赛,尽全力去拿分(不会打暴力

  2. 不够谨慎,数据范围开小(四道题,我三题见了祖宗)

  3. 对于代码的熟练度有待提高

  4. 因此在接下来的一段时间,应该训练自己打暴力,拿部分分的能力,无论会不会正解

    都应该先打暴力解法,保证等到正式比赛时可以保底

接下来讲可行解
T1:

题意:求交换(相邻两个数交换)不超过k次后,该数列的最小逆序对数

分析:对于序列中相邻两个数a1,a2(a1在前),交换后序列的逆序对数有三种情况,

​ 1.+1(a1<a2)

​ 2.0 (a1==a2)

​ 3.-1 (a1>a2)

​ (a1与原先在 a1 前面, a2 后面的数的相对位置不变,a2同理)

​ 显然,对于每次交换最优的效益即情况3

​ 现在的问题是能否保证但序列中仍存在逆序对的时候,必然存在两个相邻的数为逆序对

​ 考虑反证法:假设序列中存在a1,a2(a1在前)不相邻,则任意两个相邻数均不为逆序对,

​ 此时,由于任意两个相邻数均不为逆序对,设b1,b2为序列中任意两个相邻的数(b1在前)

​ 则满足b1<=b2,所以整个序列不降,与题设矛盾,原命题成立。

​ 因此问题转换成求原序列的逆序对对数,经典算法:分治求逆序对(然而没想到

​ 离散化+权值线段树: 对于每个数求加到它时比它大的数的个数

O ( n log ⁡ 2 n ) O(n\log_2n) O(nlog2n)

T2:dp

​ 由题意得,总代价=左边的代价+右边的代价+中间的代价

​ 中间代价=v[中间数的个数];现在问题就变成求左右的代价和。

​ 设 f [ i ] [ j ] f[i][j] f[i][j]表示 [1~l] 与 [r~n] 对应相等的最小代价

a [ i ] 表 示 合 并 i 个 的 代 价 , s u m [ i ] 表 示 1   i 的 体 积 和 a[i]表示合并i个的代价,sum[i]表示1~i的体积和 a[i]isum[i]1 i

​ 所以 转移方程为:

f [ i ] [ j ] = f [ l ] [ r ] + a [ i − l ] + a [ r − j ] , ( l < = i < j < = r , s u m [ i ] − s u m [ l ] = = s u m [ r ] − s u m [ j ] ) f[i][j]=f[l][r]+a[i-l]+a[r-j],(l<=i<j<=r,sum[i]-sum[l]==sum[r]-sum[j]) f[i][j]=f[l][r]+a[il]+a[rj],(l<=i<j<=r,sum[i]sum[l]==sum[r]sum[j])

a n s = m i n ( a n s , f [ i ] [ j ] + a [ j − i − 1 ] ) ans=min(ans,f[i][j]+a[j-i-1]) ans=min(ans,f[i][j]+a[ji1])

​ 发现由于体积是正数,所以体积序列的前缀和和后缀和均单调递增,

​ 设 q z h [ ] 表 示 前 缀 和 , h z h [ ] 表 示 后 缀 和 , qzh[]表示前缀和,hzh[]表示后缀和, qzh[],hzh[]

​ 则但且仅当 q z h [ i ] = = h z h [ j ] ( i < j ) 时 转 移 qzh[i]==hzh[j](i<j)时转移 qzh[i]==hzh[j]i<j

​ 因为 ∀ i ( i ∗ 2 < n ) , 总 有 至 多 1 个 j ( j > n / 2 ) 使 得 q z h [ i ] = = h z h [ j ] \forall i(i*2<n),总有至多1个j(j>n/2)使得qzh[i]==hzh[j] i(i2<n),1j(j>n/2)使qzh[i]==hzh[j],

​ 所以只需要预处理出每个 i 对 应 的 j i对应的j ij,再枚举 i i i即可,易知,最多有 n / 2 n/2 n/2 i i i

​ 所以时间复杂度为 O ( n 2 ) O(n^2) O(n2)

T3:数据结构题,同样是权值线段树,

​ [L,R]三种情况,设l为L的种类,r为R的种类

​ 1.l == r D: 单点修改 Q: 单点查询

​ 2.l != r D: [l,l],[r,r]单点修改;[l+1,r-1]区间修改(l+1<=r-1)

​ Q: [l,l],[r,r]单点查询,[l+1,r-1]区间查询(l+1<=r-1)

O ( m log ⁡ 2 n ) O(m\log_2n) O(mlog2n)

T4:操作解释: 设 两 个 集 合 A , B , ∃   a ∈ A , b ∈ B , 质 数 p > P ,   p ∣ a   且   p ∣ b , A , B 可 以 合 并 设两个集合A,B,{\quad}\exist {\,}a \in A ,b \in B,质数p>P, {\,}p|a{\,}且{\,}p|b,A,B可以合并 A,B,aA,bB,p>P,papb,A,B

​ 分析:[A,B] (A,B与上面意义不同)

​ B<=A+106 => B-A<=106,

      令 a = p ∗ i ,   b = p ∗ ( i + 1 )   ( a ∈ [ A , B ] ,   i ! = 0 ,   p 为 质 数 ) , a , b 同 时 存 在 于 [ A , B ] , 当 且 仅 当    b = a + p < = B    {\quad\,\,\,\,\,}令a=p*i,{\,}b=p*(i+1){\,}(a\in [A,B],{\,}i!=0,{\,}p为质数),a,b同时存在于[A,B],当且仅当{\,\,}b=a+p<=B{\,\,} a=pib=p(i+1)(a[A,B],i!=0,p)a,b[A,B],b=a+p<=B

      即    p < = B − a , ∵ B − a < = B − A < = 1 0 6 ∴ 只 需 考 虑 [ 2 , 1 0 6 ] 中 的 质 数 即 可 {\quad\,\,\,\,\,}即{\,\,}p<=B-a,\because B-a<=B-A<=10^6 \therefore 只需考虑[2,10^6]中的质数即可 p<=BaBa<=BA<=106[2,106],

​ 想到并查集+线筛, 由于太弱不会计算时间复杂度


知识点回忆:

1.对拍:

​ (1). 头文件 <windows.h>

​ (2). system(“程序名 < 输入文件名 > 输出文件名”);system(“fc 输出文件名 答案文件名”);

#include<cstdio>
#include<windows.h>
#include<cstdlib>
using namespace std;
int main(){
    system("程序名 < 输入文件名 > 输出文件名");
        system("fc 输出文件名 答案文件名");
    return 0;
}

代码

T1:

#include<cstdio>
#include<algorithm>
#include<cstring> 
#define ll long long
using namespace std;
const int N=100000;
int n,k,xl[N+5],num[N+5],tre[N*32+5],tot=0,cnt=0;
int L,R;ll ans=0;
void add(int l,int r,int x){
	if(l==r && r==L){
		tre[x]++;
		return ; 
	}
	int mid=l+r>>1;
	if(L<=mid)	add(l,mid,x*2);
	else
		add(mid+1,r,x*2+1);
	tre[x]=tre[x*2]+tre[x*2+1];	
}
int burry(int lim){
	int mid,l=1,r=n-cnt;
	while(l<=r){
		mid=l+r>>1;
		if(xl[mid]>lim)
			r=mid-1;
		else{
			if(xl[mid]==lim)
				return mid;
			l=mid+1;
		}
	}
}
ll find(int l,int r,int x){
	ll ret=0;
	if(l>=L && r<=R)
		return tre[x];
	int mid=l+r>>1;
	if(L<=mid)	ret+=find(l,mid,x*2);
	if(R>mid) ret+=find(mid+1,r,x*2+1);
	return ret;
}
int main(){
	memset(tre,0,sizeof tre);
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++){
		scanf("%d",&num[i]);
		xl[i]=num[i];
	}
	sort(xl+1,xl+n+1);
	cnt=0; xl[0]=-1;
	for(int i=1;i<=n;i++)	
		if(xl[i]==xl[i-1]) cnt++;
		else
			xl[i-cnt]=xl[i];
	/*for(int i=1;i<=n-cnt;i++){
		printf("%d ",xl[i]);
	}*/
	for(int i=1;i<=n;i++){
		int cur=burry(num[i]);
		L=cur+1;R=n-cnt;
		ans+=find(1,n-cnt,1);
		L=cur;
		add(1,n-cnt,1);
	}
	printf("%lld",ans>k?ans-k:0);
	return 0;
}

T2:

#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int N=10000;
struct node{
	int l,r;
}eq[N+5];
ll f[N+5],qz[N+5],v[N+5],val[N+5],ans;
int n,tot=0;
ll min(ll a,ll b){
	return a<b?a:b;
}
int main(){
	memset(f,1,sizeof f);ans=f[0];
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld",&v[i]);
		qz[i]=qz[i-1]+v[i];
	}
	int ls=n,fr=1;ll cnt=0;
	for(int i=1;i<=n;i++){
		cnt+=v[ls];
		while(cnt>qz[fr] && fr<=ls)	fr++;
		if(cnt==qz[fr] && fr<ls){
			eq[++tot].l=fr;
			eq[tot].r=ls;
//			printf("%d %d %d\n",tot,fr,ls);
			fr++;
		}
		ls--;
		scanf("%lld",&val[i]);
	}
	f[0]=0;eq[0].l=0;eq[0].r=n+1;
	for(int i=1;i<=tot;i++){
		for(int j=0;j<i;j++){
			int d_i=eq[i].l-eq[j].l,d_j=eq[j].r-eq[i].r;
//			printf("%d %d\n",d_i,d_j);
			f[i]=min(f[i],f[j]+val[d_i]+val[d_j]);
			ans=min(f[i]+val[eq[i].r-eq[i].l-1],ans);
		}
	}
	int i=eq[tot].l+1,j=eq[tot].r-1;
	printf("%lld",min(ans,val[n]));
	return 0;
} 

T3:

#include<cstdio>
#include<cstring>
#define ll long long 
using namespace std;
const int N=100000;
struct node{
	ll sum,mx,tag;
	node() {tag=0;} 
}tre[N*32+5];
ll n,m,L,R,lim,ad1,ad2,ad3,ans;
ll max(ll a,ll b){
	return a>b?a:b;
}
ll get(int l,int r,int x,ll ord){//查询序列第k个 
	if(l==r){
		if(!ad1)	ad1=tre[x].sum-ord+1;
		else{
			ad2=tre[x].sum-ord+1;
			ad3=ord;
		}
		return l; 
	} 
	int mid=l+r>>1;
	if(tre[x].tag){
		tre[x*2].sum*=1<<tre[x].tag;tre[x*2+1].sum*=1<<tre[x].tag;
		tre[x*2].mx*=1<<tre[x].tag;tre[x*2+1].mx*=1<<tre[x].tag;
		tre[x*2].tag+=tre[x].tag;tre[x*2+1].tag+=tre[x].tag;tre[x].tag=0;
	}
	if(ord<=tre[x*2].sum)	return get(l,mid,x*2,ord);
	else
		return get(mid+1,r,x*2+1,ord-tre[x*2].sum);
}
ll find(int l,int r,int x){
	if(l>=L && r<=R){
		return tre[x].mx;
	}
	int ret=0;
	int mid=l+r>>1;
	if(tre[x].tag){
		tre[x*2].sum*=1<<tre[x].tag;tre[x*2+1].sum*=1<<tre[x].tag;
		tre[x*2].mx*=1<<tre[x].tag;tre[x*2+1].mx*=1<<tre[x].tag;
		tre[x*2].tag+=tre[x].tag;tre[x*2+1].tag+=tre[x].tag;tre[x].tag=0;
	}
	if(L<=mid)	ret=max(find(l,mid,x*2),ret);
	if(R>mid) ret=max(find(mid+1,r,x*2+1),ret);
	tre[x].sum=tre[x*2].sum+tre[x*2+1].sum;
	tre[x].mx=max(tre[x*2].mx,tre[x*2+1].mx);
	return ret;
}
void add(int l,int r,int x){
	if(l>=L && r<=R){
		tre[x].sum<<=1;
		tre[x].mx<<=1;
		tre[x].tag++;
		return ;
	}
	int mid=l+r>>1;
	if(tre[x].tag){
		tre[x*2].sum*=1<<tre[x].tag;tre[x*2+1].sum*=1<<tre[x].tag;
		tre[x*2].mx*=1<<tre[x].tag;tre[x*2+1].mx*=1<<tre[x].tag;
		tre[x*2].tag+=tre[x].tag;tre[x*2+1].tag+=tre[x].tag;tre[x].tag=0;
	}
	if(L<=mid)	add(l,mid,x*2);
	if(R>mid) add(mid+1,r,x*2+1);
	tre[x].sum=tre[x*2].sum+tre[x*2+1].sum;
	tre[x].mx=max(tre[x*2].mx,tre[x*2+1].mx);
	return ;
}
void sing_add(int l,int r,int x,ll js){
	if(l>=L && r<=R){
		tre[x].sum+=js;
		tre[x].mx=tre[x].sum;
		return ;
	}
	int mid=l+r>>1;
	if(tre[x].tag){
		tre[x*2].sum*=1<<tre[x].tag;tre[x*2+1].sum*=1<<tre[x].tag;
		tre[x*2].mx*=1<<tre[x].tag;tre[x*2+1].mx*=1<<tre[x].tag;
		tre[x*2].tag+=tre[x].tag;tre[x*2+1].tag+=tre[x].tag;tre[x].tag=0;
	}
	if(L<=mid)	sing_add(l,mid,x*2,js);
	else
		sing_add(mid+1,r,x*2+1,js);
	tre[x].sum=tre[x*2].sum+tre[x*2+1].sum;
	tre[x].mx=max(tre[x*2].mx,tre[x*2+1].mx);
	return ;
}
void build(int l,int r,int x){
	tre[x].tag=0;
	if(l==r){
		tre[x].sum=1;
		tre[x].mx=1;
		return ;
	}
	int mid=l+r>>1;
	build(l,mid,x*2);build(mid+1,r,x*2+1);
	tre[x].sum=tre[x*2].sum+tre[x*2+1].sum;
	tre[x].mx=max(tre[x*2].mx,tre[x*2+1].mx);
	return;
}
int main(){
//	freopen("experiment1.in","r",stdin); 
//	freopen("T3.out","w",stdout);
	scanf("%d%d",&n,&m);
	build(1,n,1);
	for(int i=1;i<=m;i++){
		char a;
		scanf(" %c%lld%lld",&a,&L,&R);
		ad1=ad2=ad3=0;ans=0;
		int l=get(1,n,1,L),r=get(1,n,1,R);
		if(a=='D'){//翻倍 
			if(l==r){
				L=R=r;
				sing_add(1,n,1,ad1-ad2+1); 
			}
			else{
				L=R=l;
				sing_add(1,n,1,ad1);
				L=R=r;
				sing_add(1,n,1,ad3);
				if(l+1!=r){
					L=l+1,R=r-1; 
					add(1,n,1);
				}
			}
		}
		else{//
			if(l==r)
				ans=ad1-ad2+1; 
			else{
				ans=max(ad1,ad3);
				if(l+1!=r){
					L=l+1;R=r-1;
					ans=max(ans,find(1,n,1));
				}
			}
			/*if(ans==1 && i!=1){
				printf("%lld\n",i);
				return 0;
			}*/
			printf("%lld\n",ans);
		}
	}
	return 0;
}

T4:

#include<cstdio>
#include<cstring>
#define ll long long 
using namespace std;
const int N=1000000;
ll A,B;
ll ss[N+5],p,tot=0,fa[N+5],n,del,ans;
bool vis[N+5];
ll getfa(ll x){
	return fa[x]==x?x:fa[x]=getfa(fa[x]);
} 
void merg(int a,int b){
	int f1=getfa(fa[a]),f2=getfa(fa[b]);
	if(f1>f2)	fa[f1]=f2;
	else
		fa[f2]=f1;
}
int main(){
	scanf("%lld%lld%lld",&A,&B,&p); 
	vis[1]=1;n=B-A+1;fa[1]=1;
	for(int i=2;i<=n;i++){
		fa[i]=i;//初始化 
		if(!vis[i])
			ss[++tot]=i;
		for(int j=1;j<=tot && i*ss[j]<=n;j++){
			vis[i*ss[j]]=1;
			if(i%ss[j]==0)
				break;
		}
	}
//	printf("%d ",tot);
	del=A-1;
	for(int i=1;i<=tot;i++){
		if(ss[i]<p)	continue;
		int las=0;
		ll j=ss[i]-(A%ss[i]==0?ss[i]:A%ss[i])+1;
		for(;j<=n;j+=ss[i]){
			if((j+del)%ss[i]==0){
				if(las) merg(j,las);
				las=j;
			}	
		}
	}	
	memset(vis,0,sizeof vis);
	for(int i=1;i<=n;i++){
		fa[i]=getfa(fa[i]);
		if(!vis[fa[i]]){
			ans++;
			vis[fa[i]]=1;
		}
	}
	printf("%lld",ans);
	return 0;
}


禁止转载

​ 2021.11.14

Update:

11.21 补充T2

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值