目录
C. Mark and His Unfinished Essay
A. Mark the Photographer
思路:按顺序排列一下,为了让答案成立,贪心一下,让第i位与第i+n位对其,看是否满足题目条件即可.
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N =5e5+10,mod=998244353;
bool cmp(int a,int b)
{
return a<b;
}
int a[105];
void solve()
{
int n,x;
cin>>n>>x;
for(int i=1;i<=2*n;i++)
cin>>a[i];
sort(a+1,a+1+2*n,cmp);
for(int i=1;i<=n;i++)
{
if(a[i+n]-a[i]<x)
{
cout<<"NO\n";
return ;
}
}
cout<<"YES\n";
return ;
}
signed main()
{
int t;
cin>>t;
while(t--)
solve();
return 0;
}
B. Mark the Dust Sweeper
思路:这个操作就是选一个区间,把区间开头的值减一,在区间的右端点+1的地方加上1,所以我们只需要先把所有正数之间的0填满,一个0产生一点贡献,然后就会得到一个连续的整数区间,这个正数区间按照操作就可以一直对开头减一,直到全部移动到最后一位.所以结果就是在2到(n-1)的位置上所有正数的和与0的个数求和.
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N =5e5+10,mod=998244353;
void solve()
{
int x,n,f=0,sum=0,cnt0=0;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>x;
if(i==n)
continue;
if(x)
f=1,sum+=x;
if(f!=0&&x==0)
cnt0++;
}
cout<<sum+cnt0<<"\n";
}
signed main()
{
int t;
cin>>t;
while(t--)
solve();
return 0;
}
C. Mark and His Unfinished Essay
思路:我进行了预处理,对于每一段新取的区间,我们都可以得到这个区间原来所在上一次操作后区间内的位置.所以我们对于每个新产生的区间都记录一个他们在上一次操作完成后取的左右端点(preL,preR),在记录一个进行完操作后该区间所在的字符串的位置(Lnow,Rnow).
然后我们每次对于k所在的区域,可以用(Lnow-preL)来求出该区间截取前每个点的对应距离,所以也可以求出k所在字符串粘贴到字符串之前,复制的那一段上k的位置,这样一直往前找,最终一定会回到原字符串的区间上,数据也不打,直接预处理了暴力跑就行了.
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N =5e5+10,mod=998244353;
struct node
{
int lnow,rnow,lpre,rpre;
};
vector<node>ve;
void solve()
{
ve.clear();
string s;
int n,c,q,l,r,l1,r1;
cin>>n>>c>>q;
cin>>s;
s="z"+s;
ve.push_back({1,n,-1,-1});
for(int i=0;i<c;i++)
{
int tot=ve.size();
tot--;
cin>>l>>r;
l1=ve[tot].rnow+1;
r1=l1+r-l;
ve.push_back({l1,r1,l,r});
}
int k;
while(q--)
{
cin>>k;
while(k>n)
{
int temp;
for(int i=0;i<ve.size();i++)
{
if(ve[i].lnow<=k&&k<=ve[i].rnow)
{
k-=(ve[i].lnow-ve[i].lpre);
break;
}
}
}
cout<<s[k]<<"\n";
}
return ;
}
signed main()
{
std::ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--)
solve();
return 0;
}
D. Mark and Lightbulbs
思路:首先可以确定的是,两个字符串的首位和末尾是肯定不能改变的.如果两者不同的话就直接输出-1.
然后就是观察这个规则,其实这个规则的本质挺好想的,每一段独立的1都可以任意变换长短和位置(前提:不能够越过别的1,两段1之间用0分隔开,不能合并成一段1):比如0100010,你可以任意变化为 011010,010110,(注意首尾不可以改变).那么问题就好解决了,我们只需要两个字符串首位相等,并且连续的0字符字段数还有1字符字段数都相等即可.输出结果的话就直接用第几段的1字符的首尾计算距离即可.(假设要做到字符串1第二段1字符串和字符串2的第二段字符串1相等,只需要把第一个字符串的第二段1字符串的左右端点分别与第二个字符串的第二段字符串1的左右端点相等即可,那么计算公式为:len=abs(r1-r2)+abs(l1-l2)).
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N =5e5+10,mod=998244353;
struct node
{
int l,r;
}edge[200005];
vector<node>v1,v2;
void solve()
{
v1.clear();
v2.clear();
string s1,s2;
int n;
cin>>n>>s1>>s2;
if(s1[0]!=s2[0]||s1[n-1]!=s2[n-1])
{
cout<<"-1\n";
return ;
}
int l1=0,l2=0,cnt10=0,cnt20=0;
if(s1[0]=='0')
cnt10++;
if(s2[0]=='0')
cnt20++;
for(int i=1;i<n;i++)
{
if(s1[i]!=s1[i-1])
{
if(s1[i]=='0')
v1.push_back({l1,i-1});
else
cnt10++;
l1=i;
}
if(s2[i]!=s2[i-1])
{
if(s2[i]=='0')
v2.push_back({l2,i-1});
else
cnt20++;
l2=i;
}
}
if(s1[n-1]=='1')
v1.push_back({l1,n-1});
else
cnt10++;
if(s2[n-1]=='1')
v2.push_back({l2,n-1});
else
cnt20++;
if(v1.size()!=v2.size()||cnt10!=cnt20)
{
cout<<"-1\n";
return ;
}
int ans=0;
for(int i=0;i<v1.size();i++)
{
ans+=abs(v1[i].l-v2[i].l)+abs(v1[i].r-v2[i].r);
}
cout<<ans<<"\n";
return ;
}
signed main()
{
int t;
cin>>t;
while(t--)
solve();
return 0;
}