Codeforces Round #699 (Div. 2)
A
题目大意,给一个操作序列,问能否通过删除一些操作使你从\((0,0)\)走向\((x,y)\)
只保留符号相同的操作,模拟一遍看看能不能达到给定的坐标。
#include <bits/stdc++.h>
#define N 1000009
using namespace std;
typedef long long ll;
int x,y,n;
char s[N];
inline ll rd(){
ll x=0;char c=getchar();bool f=0;
while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f?-x:x;
}
int main(){
int T=rd();
while(T--){
x=rd();y=rd();
scanf("%s",s+1);n=strlen(s+1);
int cnt1=0,cnt2=0;
for(int i=1;i<=n;++i){
if(x<0&&s[i]=='L'){
cnt1++;
}
if(x>0&&s[i]=='R'){
cnt1++;
}
if(y<0&&s[i]=='D'){
cnt2++;
}
if(y>0&&s[i]=='U'){
cnt2++;
}
}
if(cnt1>=abs(x)&&cnt2>=abs(y))
printf("YES\n");
else printf("NO\n");
}
return 0;
}
B
垃圾模拟
#include <bits/stdc++.h>
#define N 1000009
using namespace std;
typedef long long ll;
int n,k;
int a[N];
inline ll rd(){
ll x=0;char c=getchar();bool f=0;
while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f?-x:x;
}
int main(){
int T=rd();
while(T--){
n=rd();k=rd();
for(int i=1;i<=n;++i)a[i]=rd();
int bo=0;int ans=0;
for(int i=1;i<=k;++i){
int bm=0;
for(int j=1;j<n;++j){
if(a[j]>=a[j+1])continue;
else {bm=1;a[j]++;ans=j;break;}
}
if(!bm){bo=1;break;}
}
if(bo){printf("-1\n");}
else printf("%d\n",ans);
}
return 0;
}
C
给定初始颜色序列和最终目标颜色序列,再给定\(m\)个人,每个人必须按顺序给某一个格子染上\(c_i\)的颜色,问是否可以染成目标序列。
我们可以将操作序列倒着考虑,如果能够一步染成目标序列那么优先染那个,否则我们可以考虑将其染到初始序列和目标序列颜色相同的位置上,或者我们可以染到之前已经被染过的地方(因为操作是可以覆盖的)。
当有一步不能染色或者最终没有染成目标序列时无解。
#include <bits/stdc++.h>
#define N 100009
using namespace std;
typedef long long ll;
set<int>vec[N];
int ans[N],a[N],b[N],c[N],rbs[N];
int n,m;
inline ll rd(){
ll x=0;char c=getchar();bool f=0;
while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f?-x:x;
}
int main(){
int T=rd();
while(T--){
n=rd();m=rd();
for(int i=1;i<=n;++i)a[i]=rd();
for(int i=1;i<=n;++i){
b[i]=rd();
if(b[i]!=a[i])vec[b[i]].insert(i);
else rbs[b[i]]=i;
}
for(int i=1;i<=m;++i)c[i]=rd();
int tg=0,bm=0;
for(int i=m;i>=1;--i){
if(!vec[c[i]].empty()){
ans[i]=*vec[c[i]].begin();
tg=ans[i];
vec[c[i]].erase(vec[c[i]].begin());
}
else if(rbs[c[i]]){
ans[i]=rbs[c[i]];tg=ans[i];
}
else if(tg){
ans[i]=tg;
}
else bm=1;
}
for(int i=1;i<=n;++i)if(!vec[i].empty())bm=1;
if(bm){printf("NO\n");}
else{
printf("YES\n");
for(int i=1;i<=m;++i)printf("%d ",ans[i]);
puts("");
}
for(int i=1;i<=n;++i){
vec[i].clear();rbs[i]=0;
}
}
return 0;
}
D
题目大意:给定一张\(n\)个点的无向图,每条边上有一个字母\(a\)或\(b\),问是否存在一条长度为\(m\)的回文路径,并且输出方案。
首先我们不难发现当存在点对\((i,j)\)使得来回的路径上的字母相同,那么我们一直来回地走这条边就好了,这样一定是回文的。
进一步的,我们还可以发现当\(m\)为奇数时,来回地走一条边同样也是回文的。
当这两种情况考虑过后,我们只剩下\(m\)为偶数且任意点对来回的字符都是相反的情况。
我们可以枚举\(i\)点,找到一组\((j,k)\)使得\((i,j)!=(i,k)\)这样我们可以在\((i,j)\)之间走\(m/2\)步,在\((i,k)\)之间走\(m/2\)步,因为对称性所以走出来的一定是回文串。
时间复杂度\(O(n^2)\)
#include <bits/stdc++.h>
#define N 1009
using namespace std;
typedef long long ll;
int n,m;
char s[N][N];
inline ll rd(){
ll x=0;char c=getchar();bool f=0;
while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f?-x:x;
}
int main(){
int T=rd();
while(T--){
n=rd();m=rd();
for(int i=1;i<=n;++i){
scanf("%s",s[i]+1);
}
int tg=0;
for(int i=1;i<=n;++i){
for(int j=1;j<i;++j){
if(s[i][j]==s[j][i]){
printf("YES\n");
for(int k=1;k<=m+1;++k){
if(k&1)printf("%d ",i);
else printf("%d ",j);
}
puts("");
tg=1;break;
}
}
if(tg)break;
}
if(!tg&&(m&1)){
printf("YES\n");
for(int i=1;i<=m+1;++i){
if(i&1)printf("1 ");
else printf("2 ");
}
puts("");
tg=1;
}
if(!tg){
for(int i=1;i<=n;++i){
int pre=0,tt=0;
for(int j=1;j<=n;++j){
if(i==j)continue;
if(!pre)pre=s[i][j],tt=j;
else if(s[i][j]!=pre){
printf("YES\n");
if((m/2)&1){
for(int k=1;k<=m/2;++k)
if(k&1)printf("%d ",tt);
else printf("%d ",i);
printf("%d ",i);
for(int k=1;k<=m/2;++k)
if(k&1)printf("%d ",j);
else printf("%d ",i);
}
else{
for(int k=1;k<=m/2;++k)
if(k&1)printf("%d ",i);
else printf("%d ",tt);
printf("%d ",i);
for(int k=1;k<=m/2;++k)
if(k&1)printf("%d ",j);
else printf("%d ",i);
}
puts("");
tg=1;break;
}
}
if(tg)break;
}
}
if(!tg)printf("NO\n");
}
return 0;
}
E
题目大意:给定一个序列,每个位置的书都有一个颜色,每次操作可以将一本书放在最后,问将序列操作成相同颜色的书放在一起最少需要几步。
我们可以补集转化一下,问题变成求出不用移动的最大个数。
那么我们考虑两种不用移动的颜色,对于每一种颜色我们维护出最左和最右的端点,那么如果两种颜色都不用移动,那么它们一定不能相交。
于是变成了区间覆盖问题,用\(BIT\)查询即可。
但是这样会有一点问题,就是在右边一些位置是不用移动的,于是我们可以维护一下后缀最大值,在\(dp\)转移的时候更新答案(具体实现还是看代码比较好)。
#include <bits/stdc++.h>
#define N 500009
using namespace std;
typedef long long ll;
int n,m;
int l[N],r[N],tr[N],a[N],cnt[N],mxr[N],lim[N],num[N];
inline ll rd(){
ll x=0;char c=getchar();bool f=0;
while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f?-x:x;
}
inline void add(int x,int y){
while(x<=n)tr[x]=max(tr[x],y),x+=x&-x;
}
inline int query(int x){
int ans=0;
while(x)ans=max(ans,tr[x]),x-=x&-x;
return ans;
}
int main(){
n=rd();
for(int i=1;i<=n;++i)a[i]=rd(),l[i]=n+1;
int mx=0;
for(int i=n;i>=1;--i){
cnt[a[i]]++;
l[a[i]]=min(l[a[i]],i);
r[a[i]]=max(r[a[i]],i);
mx=max(mx,cnt[a[i]]);
mxr[i]=mx;
}
for(int i=1;i<=n;++i){
num[r[i]]=cnt[i];
lim[r[i]]=l[i];
}
for(int i=1;i<=n;++i)if(lim[i]){
int nm=query(lim[i]-1)+num[i];
mx=max(mx,nm+mxr[i+1]);
add(i,nm);
}
cout<<n-mx;
return 0;
}
F
一个奇妙构造。