2021.11.13 B组总结
赛时:
九点多开始,一看题面,好家伙,线段树专题???
T1:
逆序对,em ,105可以做,在开始30min左右,看出正解
T2:
感觉是用字符串(回文串)相关知识,没感觉
T3:
想到线段树,但是在实现出现问题,并且时间不足
T4:
到最后,发现自己没看懂题,我蒻爆了
最后只交了T1代码,思路正确,但是不开long long 见祖宗 60pts
综上来看,
-
得分意识较低,没有认真对待每一场比赛,尽全力去拿分(
不会打暴力) -
不够谨慎,数据范围开小(四道题,我三题见了祖宗)
-
对于代码的熟练度有待提高
-
因此在接下来的一段时间,应该训练自己打暴力,拿部分分的能力,无论会不会正解
都应该先打暴力解法,保证等到正式比赛时可以保底
接下来讲可行解
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]表示合并i个的代价,sum[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[i−l]+a[r−j],(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[j−i−1])
发现由于体积是正数,所以体积序列的前缀和和后缀和均单调递增,
设 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(i∗2<n),总有至多1个j(j>n/2)使得qzh[i]==hzh[j],
所以只需要预处理出每个 i 对 应 的 j i对应的j i对应的j,再枚举 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,∃a∈A,b∈B,质数p>P,p∣a且p∣b,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=p∗i,b=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<=B−a,∵B−a<=B−A<=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