最近VP了2021年的昆明区域赛,补几道题并总结一下教训
前言

本次总共写出来四题,其中
H
H
H题大水题一个,
I
I
I题是计算几何二维平面中的板子题而且以前写过,所以在还算比较快的时间开出来了,
J
J
J题强行当了一把
d
d
l
ddl
ddl战神,样例都没测就交了,
L
L
L题连结论都没有好好推出来,就让
s
g
l
y
sgly
sgly上机写了个树状数组,稀里糊涂的过了
A.AC
反悔贪心
用
c
[
i
]
c[i]
c[i]表示将字符串第
i
i
i位和第
i
+
1
i+1
i+1位分别变为
a
a
a和
c
c
c所需要的操作数,相邻的
c
[
i
]
c[i]
c[i]不能重复拿取,于是问题就转换成了种树
时间复杂度
O
(
n
l
o
g
(
n
)
)
O(nlog(n))
O(nlog(n))
#include<bits/stdc++.h>
#define fi first
#define gcd __gcd
#define se second
#define pb push_back
#define lowbit(x) x&-x
#define inf 0x3f3f3f3f
#define mem(x,y) memset(x,y,sizeof(x))
#define lrb666 ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
typedef pair<int,int> PII;
const int maxn=5e5+7;
int n,k,c[maxn],pre[maxn],nex[maxn],vis[maxn],ans=0,num=0,flag[maxn];
char s[maxn];
void del(int x)
{
vis[x]=1;
pre[nex[x]]=pre[x];
nex[pre[x]]=nex[x];
}
int main()
{
priority_queue<PII,vector<PII>,greater<PII>>q;
scanf("%d%d",&n,&k);
scanf("%s",s+1);
for(int i=1;i<n;i++)
{
if(s[i]=='a' && s[i+1]=='c') c[i]=0;
else if(s[i]=='a' || s[i+1]=='c') c[i]=1;
else c[i]=2;
q.push({c[i],i});
pre[i]=i-1;nex[i]=i+1;
}
c[0]=inf;c[n]=inf;
while(!q.empty())
{
if(ans==n/2) break;
while(vis[q.top().se]) q.pop();
if(num+q.top().fi>k) break;
int u=q.top().se;
q.pop();
num+=c[u];
flag[u]++;
c[u]=c[pre[u]]+c[nex[u]]-c[u];
q.push({c[u],u});
del(pre[u]);del(nex[u]);
ans++;
}
for(int i=1;i<n;i++)
{
if(!flag[i] ) continue;
for(int j=i-flag[i]+1;j<i+1+flag[i];j+=2)
{
s[j]='a';s[j+1]='c';
}
}
printf("%d\n",ans);
for(int i=1;i<=n;i++) printf("%c",s[i]);
}
C. Cities
对于一个长度为
m
m
m且没有相同元素的区间,需要操作
m
−
1
m-1
m−1次才可维护
对于一段颜色相同的区间,其意义完全等同于一个独立的点,于是贪心的将相同区间缩为一个点
考虑区间
d
p
dp
dp来维护答案,
d
p
[
l
]
[
r
]
dp[l][r]
dp[l][r]表示将
[
l
,
r
]
[l,r]
[l,r]颜色全部变为
c
[
r
]
c[r]
c[r]的最小操作次数
所以若区间
[
l
,
r
]
[l,r]
[l,r]没有相同的元素,
d
p
[
l
]
[
r
]
=
d
p
[
l
]
[
r
−
1
]
+
1
dp[l][r]=dp[l][r-1]+1
dp[l][r]=dp[l][r−1]+1
若
r
r
r和
k
k
k颜色相同,则有
d
p
[
l
]
[
r
]
=
d
p
[
l
]
[
k
]
+
d
p
[
k
+
1
]
[
r
]
dp[l][r]=dp[l][k]+dp[k+1][r]
dp[l][r]=dp[l][k]+dp[k+1][r]
由于同一个数字出现的次数不超过
15
15
15次,所以时间复杂度
O
(
n
2
∗
15
)
O(n^2*15)
O(n2∗15)
#include<bits/stdc++.h>
#define fi first
#define gcd __gcd
#define se second
#define pb push_back
#define lowbit(x) x&-x
#define inf 0x3f3f3f3f
#define mem(x,y) memset(x,y,sizeof(x))
#define lrb666 ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
typedef pair<int,int> PII;
const int maxn=2e5+7;
int n,a[5005],f[5005][5005],vis[5005],nex[5005];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
vis[i]=0;nex[i]=0;
}
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i]==a[i-1])
{
i--;n--;
}
}
for(int i=1;i<=n;i++)
{
f[i][i]=0;
nex[i]=vis[a[i]];
vis[a[i]]=i;
}
for(int len=2;len<=n;len++)
{
for(int l=1;l+len-1<=n;l++)
{
int r=l+len-1;
f[l][r]=f[l][r-1]+1;
for(int k=nex[r];k>=l;k=nex[k])
{
f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]);
}
}
}
printf("%d\n",f[1][n]);
}
}
G.Gift
对于每个朋友,我们有三种选择,给他做蛋糕,给他送礼物,什么都不送
令
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k]表示对于前
i
i
i个朋友,为其中的
j
j
j个人准备礼物,在今年的第
k
k
k天时可以收获的最大价值
给朋友
i
i
i做蛋糕
f
[
i
]
[
j
]
[
k
]
=
m
a
x
(
f
[
i
]
[
j
]
[
k
]
,
f
[
i
−
1
]
[
j
]
[
k
−
c
]
+
v
)
f[i][j][k]=max(f[i][j][k],f[i-1][j][k-c]+v)
f[i][j][k]=max(f[i][j][k],f[i−1][j][k−c]+v)
给朋友
i
i
i送礼物
f
[
i
]
[
j
]
[
k
]
=
m
a
x
(
f
[
i
]
[
j
]
[
k
]
,
f
[
i
−
1
]
[
j
−
1
]
[
k
]
)
f[i][j][k]=max(f[i][j][k],f[i-1][j-1][k])
f[i][j][k]=max(f[i][j][k],f[i−1][j−1][k])
什么都不送
f
[
i
]
[
j
]
[
k
]
=
m
a
x
(
f
[
i
−
1
]
[
j
]
[
k
]
,
f
[
i
]
[
j
]
[
k
]
)
f[i][j][k]=max(f[i-1][j][k],f[i][j][k])
f[i][j][k]=max(f[i−1][j][k],f[i][j][k])
对于送礼物得到的价值,只需要二进制暴力枚举即可
时间复杂度
O
(
n
∗
m
∗
265
+
2
m
)
O(n*m*265+2^{m})
O(n∗m∗265+2m)
#include<bits/stdc++.h>
#define fi first
#define gcd __gcd
#define se second
#define pb push_back
#define lowbit(x) x&-x
#define inf 0x3f3f3f3f
#define mem(x,y) memset(x,y,sizeof(x))
#define lrb666 ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
typedef pair<int,int> PII;
const int maxn=2e5+7;
int n,m,w;
int f[2][20][400];
int year,month,day,c,v;
int d[12]={0,31,59,90,120,151,181,212,243,273,304,334};
int a[25],b[25],g[20];
struct node
{
int day,c,v;
}peop[505];
bool cmp(node a,node b)
{
return a.day<b.day;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(f,-0x3f,sizeof(f));
memset(g,0,sizeof(g));
scanf("%d%d%d",&n,&m,&w);
for(int i=1;i<=n;i++)
{
scanf("%d-%d-%d",&year,&month,&day);
scanf("%d%d",&c,&v);
if(month==2 && day==29)
{
i--;n--;continue;
}
peop[i]={d[month-1]+day,c,v};
}
for(int i=1;i<=m;i++)
scanf("%d%d",&a[i],&b[i]);
for(int i=0;i<(1<<m);i++)
{
int u=0,val=0,pri=0;
for(int j=1;j<=m;j++)
{
if(i>>(j-1)&1) u++,val+=b[j],pri+=a[j];
}
if(pri>w) continue;
g[u]=max(g[u],val);
}
sort(peop+1,peop+n+1,cmp);
f[0][0][0]=0;
for(int i=1;i<=n;i++)
{
int dd=peop[i].day;
for(int j=0;j<=m&&j<=i;j++)
{
for(int k=0;k<=365;k++)
{
int u=i%2;
f[u][j][k]=max(f[u^1][j][k],f[u][j][k]);
if(j>0)
f[u][j][k]=max(f[u][j][k],f[u^1][j-1][k]);
if(k>=peop[i].c && k<=dd)
{
f[u][j][k]=max(f[u][j][k],f[u^1][j][k-peop[i].c]+peop[i].v);
}
}
}
}
int ans=0;
for(int j=0;j<=m;j++)
{
for(int k=0;k<=365;k++)
{
ans=max(ans,f[n%2][j][k]+g[j]);
}
}
printf("%d\n",ans);
}
}
J.Parallel Sort
由于是排列,所以肯定是一些数字的环组成的,对于一个环,要环上的点统一移动一步就可以归位
这只需要最多两次操作就可以实现
假设要把
(
1
,
2...
,
n
)
(1,2...,n)
(1,2...,n)变成
(
2
,
3
,
.
.
.
,
n
,
1
)
(2,3,...,n,1)
(2,3,...,n,1),只需要先变成
(
1
,
n
,
n
−
1
,
.
.
.
,
2
)
(1,n,n-1,...,2)
(1,n,n−1,...,2),再变成
(
2
,
3
,
.
.
.
,
n
,
1
)
(2,3,...,n,1)
(2,3,...,n,1)即可
赛时这题浪费挺长时间的,在数字和数字下标里迷得云里雾里,赛后看题解没想到这么简单
L.Simone and graph coloring
该题最大颜色数等价于求一个最长的下降子序列
所以用树状数组或者二分去写即可
#include<bits/stdc++.h>
#define fi first
#define gcd __gcd
#define se second
#define pb push_back
#define lowbit(x) x&-x
#define inf 0x3f3f3f3f
#define mem(x,y) memset(x,y,sizeof(x))
#define lrb666 ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
typedef pair<int,int> PII;
const int maxn=1e6+7;
int a[maxn],col[maxn],ans,n,tre[maxn];
void update(int x,int num)
{
for(;x<=n;x+=lowbit(x)) tre[x]=max(tre[x],num);
}
int query(int x)
{
int res=0;
for(;x;x-=lowbit(x)) res=max(res,tre[x]);
return res;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
ans=0;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) tre[i]=0;
for(int i=n;i>=1;i--)
{
col[i]=query(a[i]-1)+1;
update(a[i],col[i]);
ans=max(ans,col[i]);
}
printf("%d\n",ans);
for(int i=1;i<=n;i++) printf("%d ",col[i]);
puts("");
}
}
M.Stone Games
对于区间
[
L
,
R
]
[L,R]
[L,R]无法相加构造出来的最小整数
s
u
m
sum
sum,建立权值线段树,假设当前可以构造出来的一个整数为
s
u
m
sum
sum,通过查询小于
s
u
m
+
1
sum+1
sum+1的数字的数字和,则可以不断维护新的
s
u
m
sum
sum不断更新答案,对于多个
[
L
,
R
]
[L,R]
[L,R]区间上的查询,只需要主席树即可
时间复杂度
O
(
q
∗
l
o
g
(
n
)
2
)
O(q*log(n)^2)
O(q∗log(n)2)
#include<bits/stdc++.h>
#define fi first
#define gcd __gcd
#define se second
#define int long long
#define pb push_back
#define lowbit(x) x&-x
#define inf 0x3f3f3f3f
#define mem(x,y) memset(x,y,sizeof(x))
#define lrb666 ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
typedef pair<int,int> PII;
const int maxn=1e6+7;
int n,q,cnt;
int a[3*maxn],root[3*maxn];
vector<int>v;
int tr[maxn*4*20],lch[maxn*4*20],rch[maxn*4*20],sum[maxn*4*20];
int build(int l,int r)
{
int pos=++cnt;
lch[pos]=0,rch[pos]=0,sum[pos]=0;
if(l<r)
{
int mid=l+r>>1;
lch[pos]=build(l,mid);
rch[pos]=build(mid+1,r);
}
return pos;
}
int update(int pre,int l,int r,int x,int v)
{
int pos=++cnt;
lch[pos]=lch[pre],rch[pos]=rch[pre],sum[pos]=sum[pre]+v;
if(l<r)
{
int mid=l+r>>1;
if(x<=mid) lch[pos]=update(lch[pre],l,mid,x,v);
else rch[pos]=update(rch[pre],mid+1,r,x,v);
}
return pos;
}
int query(int x,int y,int l,int r)
{
if(l==r) return sum[y];
int mid=l+r>>1;
if(x<=mid) return query(x,lch[y],l,mid);
else return query(x,rch[y],mid+1,r)+sum[lch[y]];
}
int find(int num)
{
return lower_bound(v.begin(),v.end(),num)-v.begin();
}
signed main()
{
scanf("%lld%lld",&n,&q);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
v.pb(a[i]);v.pb(a[i]+1);v.pb(a[i]-1);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
int sz=v.size();
for(int i=1;i<=n;i++)
{
int x=find(a[i]);
root[i]=update(root[i-1],0,sz-1,x,a[i]);
}
int ans=0;
while(q--)
{
int l1,r1,l,r;
scanf("%lld%lld",&l1,&r1);
l=min((l1+ans)%n+1,(r1+ans)%n+1);
r=max((l1+ans)%n+1,(r1+ans)%n+1);
int num=0;
while(1)
{
int x=find(num+1);
int cal=query(x,root[r],0,sz-1)-query(x,root[l-1],0,sz-1);
if(cal<=num)
{
ans=num+1; break;
}
num=cal;
}
printf("%lld\n",ans);
}
}
希望周日的昆明加油
490





