A. Kids Seating:
题目链接:https://codeforces.ml/contest/1443/problem/A
题目大意:
在1~4∗n中选出n个数,使得任意一个数对(a,b)不满足:gcd(a,b)=1或者a|b或b|a。
题目分析:
找规律题,从4 * n - 2一直到2 * n依次减2得到的n个数一定满足题目条件
正解程序:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>
using namespace std;
typedef long long ll;
ll T,n;
int main()
{
scanf("%lld",&T);
while(T--)
{
scanf("%lld",&n);
ll times=4*n-2;
for(ll i=1;i<=n;i++)
{
printf("%lld ",times);
times-=2;
}
printf("\n");
}
return 0;
}
B.Saving the City:
题目链接:https://codeforces.ml/contest/1443/problem/B
题目大意:
给定一个01字符串,1表示地雷,0表示空地。引爆一个地雷的代价是a,引爆后左右两个地方如果有地雷也会爆炸。还可以在空地埋上地雷,每埋一个的代价是b。问引爆所有地雷的最小代价。
题目分析:
1.首先明白,连续的1是没有意义的,点爆任意一个1相当于点爆这连续的所有的1。于是我们把字符串简化为…010…010…这样的字符串。
2.现在假设对于第i
个地雷来说,前面i-1
个地雷已经引爆了,代价为sum
,这时候有仅两种选择:
- 将
i-1
到i
中间的t
个0变成1,此时引爆的代价为sum+t*b
。 - 单独点爆第
i
个地雷,此时引爆的代价为sum+a
。
3.所以我们只需要选择min(t*b,a)
。
4.不要忘记一定要把第一个点爆,所以如果存在地雷的话,其实代价应该是a
。
正解代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>
using namespace std;
typedef long long ll;
ll T,a,b,count1=0;
char str[100010],New[100010];
int main()
{
scanf("%lld",&T);
while(T--)
{
count1=0;
scanf("%lld%lld",&a,&b);
scanf("%s",str);
ll len=strlen(str);
ll t=0,flag=-1;
char ch='.';
//简化字符串
for(ll i=0;i<len;i++)
{
if(str[i]=='1')
{
flag=i;
break;
}
}
for(ll i=flag;i<len;i++)
{
if(str[i]=='1' && ch=='1')
;
else if(str[i]=='1')
New[count1++]=str[i];
else
New[count1++]=str[i];
ch=str[i];
}
New[count1]='\0';
ll ans=0;
if(flag==-1)
ans=0;
else
ans=a;
for(ll i=1;i<count1;i++)
{
if(New[i]=='1')
{
ans+=min(t*b,a);
t=0;
}
else
t++;
}
printf("%lld\n",ans);
}
return 0;
}
C. The Delivery Dilemma
题目链接:https://codeforces.ml/contest/1443/problem/C
题目大意:
一个人要买n道菜,他可以自己去拿和叫买卖送,送外卖是平行进行的,而自己拿是独立进行的。告诉你每道菜自己去拿花的时间和外卖花的时间,求买选全部菜所需的最少时间。
题目分析:
1.因为送外卖是平行的,所以如果我们选择其中m个送外卖,则耗时为m个时间中最大的那个,剩下的都被最大的覆盖了。
2.所以我们只需要枚举送外卖的最大时间,小于此时间的被覆盖,大于此时间的就人工运输即可。
3.为了提高效率,我们直接对其进行排序,并维护人工运输的前缀和。
正解代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>
using namespace std;
typedef long long ll;
const ll maxn=200010;
struct node
{
ll sel;
ll tra;
}num[maxn];
ll T,n,pre[maxn];
bool cmp(node a,node b)
{
return a.tra<b.tra;
}
int main()
{
scanf("%lld",&T);
while(T--)
{
memset(pre,0,sizeof(pre));
scanf("%lld",&n);
for(ll i=1;i<=n;i++)//外卖的时间
scanf("%lld",&num[i].tra);
for(ll i=1;i<=n;i++)//自己拿的时间
scanf("%lld",&num[i].sel);
sort(num+1,num+1+n,cmp);
for(ll i=1;i<=n;i++)
pre[i]=pre[i-1]+num[i].sel;
ll ans=1e18;
for(ll i=0;i<=n;i++)
ans=min(ans,max(num[i].tra,pre[n]-pre[i]));
printf("%lld\n",ans);
}
return 0;
}
D. Extreme Subtraction
题目链接:https://codeforces.ml/contest/1443/problem/D
题目大意:
给你一个数字,你可以执行以下操作无数次:
1、选择前若干个数,每个数都减一
2、选择后若干个数,每个数都减一
问能否将每个数都变成0
题目分析:
1.对于操作一,每次都是操作前缀,所以前面的数减一的次数一定不小于后面的数。把每个数进行操作一的次数列出来,就会形成一个非递增的序列fir。
2.对于操作一,每次都是操作后缀,所以后面的数减一的次数一定不小于前面的数。把每个数进行操作一的次数列出来,就会形成一个非递减的序列sec。
3.那么fir[i]+sec[i]=num[i](原序列)
。对于序列fir,fir[i-1]≥fir[i]
,对于序列sec,sec[i-1]≤sec[i]
,所以用num和fir将sec换元,可以得到:num[i-1]-fir[i-1]≤num[i]-fir[i]
即fir[i]≤num[i]-num[i-1]+fir[i-1]
。
4.综上0≤fir[i]≤min(fir[i-1],num[i]-num[i-1]+fir[i-1])
。因为num是定值,所以对于每个fir要尽可能取最大,能划为0的区间尽可能长。所以我们从前往后扫,如果此时fir[i]
小于0了,说明就不满足了。如果扫完都没有不满足,那么必然成立。
正解程序:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long ll;
const ll maxn=30010;
ll num[maxn],T,n;
ll fir[maxn];
int main()
{
scanf("%lld",&T);
while(T--)
{
scanf("%lld",&n);
for(ll i=1;i<=n;i++)
scanf("%lld",&num[i]);
fir[1]=num[1];
bool flag=false;
for(int i=2;i<=n;++i)
{
fir[i]=min(fir[i-1],num[i]-num[i-1]+fir[i-1]);
if(fir[i]<0)
{
flag=true;
break;
}
}
if(flag)
printf("NO\n");
else
printf("YES\n");
}
return 0;
}
E. Long Permutation
题目链接:https://codeforces.ml/contest/1443/problem/E
题目大意:
对于一个1~n的排列,每次有两个操作:
1、询问这个排序[l,r]之间的数字和是多少
2、将这个排列执行x次“下一个排列”
题目分析:
1.根据数据范围,改变的排列最多是后15位,所以前面的序列是不变的,直接求前缀和。
2.因为x次“下一个排列”一定是比现在大的,所以把排列从1排序,根据现在的排列的序号,用取余的方法得到每一个数。(排列组合的知识)
3.要从不会增加只会减少的1~n中取数,每个数用一次,所以我们用vector来维护这个动态的过程。
正解代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <vector>
using namespace std;
typedef long long ll;
const ll maxn=200010;
vector<ll> st,ans;
ll q,n,now=0;
ll pre[maxn]={0},mul[maxn];
void nextone(ll x)
{
ll cnt=0,t=1;
st.clear();
for(ll i=max(n-14,1LL);i<=n;++i)
{
st.push_back(i);
++cnt;
}
ans.clear();
for(ll i=0;i<cnt;++i)
{
ll pos=x/mul[cnt-t];
x%=mul[cnt-t];
t++;
ans.push_back(st[pos]);
st.erase(st.begin()+pos);
if(x==0)
break;
}
for(int i=0;i<st.size();i++)
ans.push_back(st[i]);
}
int main()
{
scanf("%lld%lld",&n,&q);
mul[0]=1;
for(ll i=1;i<=15;i++)
mul[i]=mul[i-1]*i;
for(ll i=1;i<=n;i++)
pre[i]=pre[i-1]+i;
for(ll i=1;i<=q;i++)
{
ll flag,l,r,x;
ll sum=0;
scanf("%lld",&flag);
if(flag==1)
{
scanf("%lld%lld",&l,&r);
nextone(now);
if(r>=n-14)
for(ll j=max(0LL,l-max(n-14,1LL));j<=r-max(n-14,1LL);j++)
sum+=ans[j];
if(l<n-14)
sum+=pre[min(r,n-15)]-pre[l-1];
printf("%lld\n",sum);
}
else
{
scanf("%lld",&x);
now+=x;
}
}
return 0;
}
F. Identify the Operations
题目链接:https://codeforces.ml/contest/1443/problem/F
题目大意:
对于一个数组a,每次你可以移出一个数,然后将他相邻的其中一个数放入另一个数组尾部(开始为空),问有几种移出的方法使最后放入的那个数组和给定的数组b一样。
题目分析:
1。很显然,对于一个在b中的数来说,此时a序列中与它相邻的两个数中一定有不在b中的数。有两个则有两种选择,一个则有一种选择,零个则不存在答案。
2.而当某个数y从a中被取走时,它一定满足相邻的数xyz有不在b中的数,假设为z。而如果它旁边的x在b中,那么这个数就继承了z这个能被取出的状态,因为此时序列变成了xz,z是不在b中的。
3.所以我们发现,当某个数被取出后,它的效果和原本不在b中的数是一样的,所以我们用一个数组来标记这个工程。
4.遍历一遍b,检查此数在a中左右两侧是否为合法状态,计算答案。
正解程序:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long ll;
const ll maxn=2e5+10;
const ll mod=998244353;
ll a[maxn],pos[maxn],b[maxn];
bool used[maxn];
ll T,n,k;;
int main()
{
scanf("%lld",&T);
while(T--)
{
memset(used,false,sizeof(used));
ll n,k;
scanf("%lld%lld",&n,&k);
for(ll i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
pos[a[i]]=i;
}
for(ll i=1;i<=k;i++)
{
scanf("%lld",&b[i]);
used[pos[b[i]]]=true;
}
ll ans=1;
for(ll i=1;i<=k;i++)
{
ll times=0;
if(pos[b[i]]>1 && !used[pos[b[i]]-1])
times++;
if(pos[b[i]]<n && !used[pos[b[i]]+1])
times++;
ans*=times;
if(ans==0)
break;
ans%=mod;
used[pos[b[i]]]=false;
}
printf("%lld\n",ans);
}
return 0;
}