A - Space Navigation
设最终走到点
(
x
,
y
)
(x,y)
(x,y),然后,删除一个
L
L
L相当于
x
+
1
x+1
x+1,删除一个
R
R
R相当于
x
−
1
x-1
x−1,删
U
U
U相当于
y
−
1
y-1
y−1,删
D
D
D相当于
y
+
1
y+1
y+1。
判断
(
x
,
y
)
(x,y)
(x,y)到点
(
p
x
,
p
y
)
(px,py)
(px,py)是否可能通过上述操作到达即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+5;
char s[N];
int main()
{
int t;scanf("%d",&t);
while(t--)
{
int l=0,r=0,u=0,d=0,x=0,y=0;
int px,py;scanf("%d%d",&px,&py);
scanf("%s",s+1);
for(int i=1;s[i];i++)
{
if(s[i]=='L') l++,x--;
if(s[i]=='R') r++,x++;
if(s[i]=='U') u++,y++;
if(s[i]=='D') d++,y--;
}
if(x==px&&y==py) printf("YES\n");
else
{
if((x>=px&&r>=x-px||px>=x&&l>=px-x)&&(y>=py&&u>=y-py||py>=y&&d>=py-y))
printf("YES\n");
else printf("NO\n");
}
}
}
B - New Colony
从左到右枚举 h i > h i − 1 h_i>h_{i-1} hi>hi−1的位置,球要扔过 h i h_i hi,就必须使得其左边的都达到高度 h i h_i hi,然后左边是一个下降的阶梯,填这个阶梯即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+5;
int n;
ll k,h[N];
int main()
{
int t;scanf("%d",&t);
while(t--)
{
scanf("%d%lld",&n,&k);
for(int i=1;i<=n;i++)
scanf("%lld",&h[i]);
h[0]=99999999999ll;
bool flag=false;
for(int i=2;i<=n;i++)
if(h[i]>h[i-1])
{
ll sum=0;
for(int j=i-1;j>=1;j--)
if(h[j]<=h[i])
sum+=h[i]-h[j];
else break;
if(sum>=k)
{
flag=true;
for(int j=i-1,len=1;j>=1;j--,len++)
{
ll x=min(h[i]-h[j],h[j-1]-h[j]);
ll s=x*len;
if(s>=k)
{
k=k-k/len*len;
if(k==0) k=len;
printf("%d\n",i-k);
break;
}
else k-=s;
}
break;
}
else
{
k-=sum;
for(int j=i-1;j>=1;j--)
if(h[j]<=h[i]) h[j]=h[i];
else break;
}
}
if(!flag) printf("-1\n");
}
}
C - Fence Painting
最后一个画家的颜色没有出现过,答案为
N
O
NO
NO。
否则,没出现过颜色的画家都刷最后一个画家刷的那块。
其次,判断一下
b
i
≠
a
i
b_i\neq a_i
bi=ai的颜色是否可以被刷好。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n,m,a[N],b[N],c[N],f[N],ans[N];
bool vis[N];
vector<int>v[N];
int main()
{
int t;scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) vis[i]=false,v[i].clear();
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]),vis[b[i]]=true,f[b[i]]=i;
for(int i=1;i<=m;i++) scanf("%d",&c[i]);
if(!vis[c[m]])
{
printf("NO\n");continue;
}
int p;
for(int i=1;i<=n;i++) if(b[i]==c[m]) p=i;
for(int i=1;i<=n;i++)
if(b[i]!=a[i]) v[b[i]].push_back(i);
if(v[c[m]].size()!=0)
{
p=v[c[m]].back();v[c[m]].pop_back();
}
for(int i=1;i<=m;i++)
{
if(!vis[c[i]]||i==m) ans[i]=p;
else
{
if(v[c[i]].size())
{
ans[i]=v[c[i]].back();
v[c[i]].pop_back();
}
else ans[i]=f[c[i]];
}
}
bool flag=true;
for(int i=1;i<=n;i++) if(v[i].size()) flag=false;
if(!flag) printf("NO\n");
else
{
printf("YES\n");
for(int i=1;i<=m;i++)
printf(i==m?"%d\n":"%d ",ans[i]);
}
}
}
D - AB Graph
当
n
n
n是奇数,随便找两个点反复横跳即可。
否则,判断是否存在
i
,
j
i,j
i,j满足
s
i
,
j
≠
s
j
,
i
s_{i,j}\neq s_{j,i}
si,j=sj,i,如果有,在
i
,
j
i,j
i,j反复横跳即可。
否则,当
n
=
2
n=2
n=2,可以证明没法得到长度为偶数的回文了。
再者,当
n
>
2
n>2
n>2,任取三个点
a
,
b
,
c
a,b,c
a,b,c,如果三个点之间有环,在环上转圈圈即可。
否则,这三个点的图一定是以下形式:
接下来手推长度为
m
m
m的回文路径:
m
=
2
:
1
→
2
→
3
,
a
a
m=2:1\rightarrow 2\rightarrow 3,aa
m=2:1→2→3,aa
m
=
4
:
2
→
3
→
2
→
1
→
2
,
a
b
b
a
m=4:2\rightarrow 3\rightarrow 2\rightarrow1 \rightarrow2,abba
m=4:2→3→2→1→2,abba
m
m
o
d
4
=
0
:
m\bmod 4=0:
mmod4=0:重复
m
=
4
m=4
m=4的情况
m
4
\frac{m}{4}
4m次。
m
=
3
:
2
→
3
→
1
→
2
,
a
b
a
m=3:2\rightarrow 3\rightarrow 1\rightarrow2,aba
m=3:2→3→1→2,aba
m
m
o
d
3
=
0
:
m\bmod 3=0:
mmod3=0:重复
m
=
3
m=3
m=3的情况
m
3
\frac{m}{3}
3m次。
m
m
o
d
3
=
1
m\bmod 3=1
mmod3=1,注意到奇数可以随便,这里
m
m
m为偶数,在最中间放一个
m
=
4
m=4
m=4的回文,这样
(
m
−
4
)
m
o
d
3
=
0
(m-4)\bmod 3=0
(m−4)mod3=0,两边放
m
=
3
m=3
m=3的回文即可。
m
m
o
d
3
=
2
m\bmod 3=2
mmod3=2,两边两个
m
=
4
m=4
m=4的回文,这样
(
m
−
8
)
m
o
d
3
=
0
(m-8)\bmod 3=0
(m−8)mod3=0,中间放一些
m
=
3
m=3
m=3的回文即可。
这样,所有答案都构造完毕。个人认为这题比
E
E
E难点,毕竟手推不容易。
E - Sorting Books
对于
.
.
.
x
l
.
.
.
x
m
.
.
.
x
r
.
.
.
...x_l...x_m...x_r...
...xl...xm...xr...,如果
x
x
x不往后扔,则一定要把它中间的全扔了。或者可以仍前面一部分的,如把
x
l
x_l
xl扔到后面变成
.
.
.
x
m
.
.
.
x
r
.
.
.
x
l
...x_m...x_r...x_l
...xm...xr...xl,此时在
x
r
x_r
xr和
x
l
x_l
xl之间的都需要往后扔。
考虑哪些不往后扔,若不扔
.
.
.
x
l
.
.
.
x
m
.
.
.
x
r
.
.
.
...x_l...x_m...x_r...
...xl...xm...xr...,则得到一个带权区间
[
l
,
r
,
v
a
l
]
[l,r,val]
[l,r,val],
v
a
l
val
val为
[
l
,
r
]
[l,r]
[l,r]区间中
x
x
x的数量,即不扔
[
l
,
r
]
[l,r]
[l,r]之间的
x
x
x可以少扔
v
a
l
val
val次。如果
x
,
y
x,y
x,y都不扔,则他们的带权区间不能相交。
问题转换为给定若干个带权区间
[
l
i
,
r
i
,
v
a
l
]
[l_i,r_i,val]
[li,ri,val],找出一些不相交的区间,使得它们的权值和最大化。(即尽可能多的
x
x
x不扔)。用树状数组即可求出答案。
最后用总数减去不扔的数量。
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int n,a[N],bit[N],val[N],l[N],r[N],vis[N];
struct node
{
int l,r,val;
node(int l,int r,int val):l(l),r(r),val(val){}
bool operator<(const node&o)const
{
if(l==o.l) return r<o.r;
return l<o.l;
}
};
vector<node>v;
int f[N];
void add(int x,int val)
{
while(x<=n) bit[x]=max(bit[x],val),x+=x&-x;
}
int query(int x)
{
int ans=0;
while(x)
ans=max(ans,bit[x]),x-=x&-x;
return ans;
}
int Dp()
{
sort(v.begin(),v.end());
int ans=0;
for(node x:v)
{
int dp=query(x.l-1)+x.val;
add(x.r,dp);
ans=max(ans,dp);
}
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),val[a[i]]++;
for(int i=1;i<=n;i++) l[i]=n+1;
for(int i=1;i<=n;i++) l[a[i]]=min(l[a[i]],i),r[a[i]]=max(r[a[i]],i);
int ans=0;
for(int i=1;i<=n;i++)
if(r[i])
{
v.push_back(node(l[i],r[i],val[i]));
ans+=val[i];
}
for(int i=n;i>=1;i--)
{
vis[a[i]]++;
v.push_back(node(i,n,vis[a[i]]));
}
int dp=Dp();
printf("%d\n",ans-dp);
}
F - AB Tree
最优的方法就是先把层数低的先涂相同的颜色,直到不能涂为止。
设一共
m
m
m层,第
i
i
i层的点数为
a
i
a_i
ai。
如果能找出一个
{
1
,
2
,
.
.
,
m
}
\{1,2,..,m\}
{1,2,..,m}的子集
{
p
1
,
p
2
,
.
.
.
,
p
k
}
\{p_1,p_2,...,p_k\}
{p1,p2,...,pk}使得
(
∑
i
=
1
k
a
p
i
)
=
x
(\sum\limits_{i=1}^ka_{p_i})=x
(i=1∑kapi)=x,则每一层都可以涂相同的颜色,答案为
m
m
m,用背包和
b
i
t
s
e
t
bitset
bitset优化即可求出这个子集。
否则,最多只有一层的颜色需要被染成两种颜色,找出叶子结点最多的一层,把不同的颜色全部染到这层的叶子结点上,这样答案只会增加
1
1
1,答案为
m
+
1
m+1
m+1,显然这是最优的答案了,同样,找出这一层后也可以用背包和
b
i
t
s
e
t
bitset
bitset优化找出子集。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m,x,y,tot,dep[N],fa[N],a[N],head[N],nex[N<<1],to[N<<1];
bool use[N],lev[N];
int clev[N];
void add(int u,int v)
{
to[++tot]=v;nex[tot]=head[u];head[u]=tot;
}
void dfs(int u)
{
lev[u]=true;
dep[u]=dep[fa[u]]+1;
a[dep[u]]++;
m=max(m,dep[u]);
for(int i=head[u];i;i=nex[i])
dfs(to[i]),lev[u]=false;
if(lev[u]) clev[dep[u]]++;
}
bitset<50001>b;
bitset<50001>c[20001],g[10];
int main()
{
scanf("%d%d",&n,&x);y=n-x;
for(int i=2;i<=n;i++)
{
scanf("%d",&fa[i]);
add(fa[i],i);
}
dfs(1);
vector<pair<int,int>>v;
for(int i=1;i<=m;i+=20000)
v.push_back({i,min(i+20000-1,m)});
b[0]=g[0][0]=1;
for(int i=1,j=0;i<=m;i++)
{
b|=b<<a[i];
if(i==v[j].second)
{
j++;
g[j]=b;
}
}
if(b[min(x,y)])
{
printf("%d\n",m);
int t=x,s=1;
if(y<x) t=y,s^=1;
for(int i=v.size();i>=1;i--)
{
int l=v[i-1].first,r=v[i-1].second;
c[0]=g[i-1];
for(int j=l;j<=r;j++)
c[j-l+1]=c[j-l]|(c[j-l]<<a[j]);
for(int j=r;j>=l;j--)
if(t>=a[j]&&c[j-l][t-a[j]])
t-=a[j],use[j]=s;
else use[j]=s^1;
}
assert(t==0);
for(int i=1;i<=n;i++)
if(use[dep[i]]) putchar('a');
else putchar('b');
}
else
{
printf("%d\n",m+1);
int mx=-1,p=-1;
for(int i=1;i<=m;i++)
if(clev[i]>mx)
mx=clev[i],p=i;
assert(p!=-1);
a[p]-=mx;
b.reset();
b[0]=1;
for(int i=1,j=0;i<=m;i++)
{
b|=b<<a[i];
if(i==v[j].second)
{
j++;
g[j]=b;
}
}
int t=x,s=1;
if(y<x) t=y,s^=1;
bool flag=false;
for(int i=t;i>=0;i--)
if(b[i]&&n-mx-i<=n-t)
{
flag=true;t=i;break;
}
assert(flag);
for(int i=v.size();i>=1;i--)
{
int l=v[i-1].first,r=v[i-1].second;
c[0]=g[i-1];
for(int j=l;j<=r;j++)
c[j-l+1]=c[j-l]|(c[j-l]<<a[j]);
for(int j=r;j>=l;j--)
if(t>=a[j]&&c[j-l][t-a[j]])
t-=a[j],use[j]=s;
else use[j]=s^1;
}
assert(t==0);
for(int i=1;i<=m;i++)
if(use[i]) x-=a[i];
else y-=a[i];
assert(x>=0);assert(y>=0);assert(x+y==mx);
for(int i=1;i<=n;i++)
if(lev[i]&&dep[i]==p)
{
clev[p]--;
if(x) putchar('a'),x--;
else
{
assert(y>=0);
putchar('b'),y--;
}
}
else if(use[dep[i]]) putchar('a');
else putchar('b');
assert(clev[p]==0);
}
}