今天浅浅vp一场7月2号的周赛
牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ
A题:游游画U
纯模拟题:我的做法是把图像看成上下两部分第一部分是1-3*n行,第二部分是后面n行,每一行的星号和点的个数是n的某个倍数(这里我就不多说了让大家想想)
直接上代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
ll n;
cin>>n;
for(int i=1;i<=n*3;i++)
{
for(int j=1;j<=n;j++)
{
cout<<"*";
}
for(int k=1;k<=2*n;k++)
{
cout<<".";
}
for(int q=1;q<=n;q++)
{
cout<<"*";
}
cout<<endl;
}
for(int i=1;i<=n;i++)
{
for(int p=1;p<=i;p++)
{
cout<<'.';
}
for(int z=1;z<=n;z++)
{
cout<<"*";
}
ll q=n*4;
q-=i;
q-=i;
q-=n;
q-=n;
for(int w=1;w<=q;w++)
{
cout<<".";
}
for(int c=1;c<=n;c++)
{
cout<<"*";
}
for(int m=1;m<=i;m++)
{
cout<<".";
}
cout<<endl;
}
}
有一说一有点丑陋
B题:游游的数组染色
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
游游拿到了一个数组,其中一些数被染成红色,一些数被染成蓝色。
游游想知道,取两个不同颜色的数,且它们的数值相等,有多少种不同的取法?
我们定义,两种取法如果取的某个数在原数组的位置不同,则定义为不同的取法。
考察乘法原理
我们只需要每一次找到一种颜色a然后把它的数字k,(mp[0][k])记录一下然后枚举每一位的答案就是:另一种颜色下数字是k的个数mp[1][k]*mp[0][k];
最后求个和就行
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
ll a[N];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
ll n;
map<int,int>mp[2];
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
string s;
cin>>s;
s='?'+s;
for(int i=1;i<=n;i++)
{
if(s[i]=='R')
{
mp[1][a[i]]++;
}
else
{
mp[0][a[i]]++;
}
}
ll res=0;
for(auto it:mp[0])
{
ll x=it.second;
ll y=mp[1][it.first];
res+=(x*y);
}
cout<<res<<endl;
}
C题:链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
游游拿到了一个01串(仅由字符'0'和字符'1'构成的字符串)。游游每次操作可以交换两个相邻的字符,例如,对于字符串"11001"而言,游游可以交换第二个字符和第三个字符变成"10101"。
游游希望最终字符串任意两个相邻的字符都不相同,她想知道最少需要多少操作次数?保证答案是有解的,即最终一定能形成任意两个相邻的字符都不相同的字符串。
我的思路是这样的因为相邻的数字不同又因为是01串所以答案无非就两种:
1.01010101....
2.10101010....
我们只需要先构造出这两个字符串然后再想一下移动后的花费就行
因为花费要最小所以我们尽量只动一次
所以我的想法就是先编号是1的是由原编号是1的移动过来的
可以简单证明一下为啥:
可以看一下上面的图假设now1是pre2变过来的,原来pre1和pre2的0只是互换了,两个位置还是0,但是pre1-now1的路程明显是pre2-now1短的,后面第2个0的位置不变说明后面的答案是没影响的,但是第一个0的代价多了一,所以按顺序移动的代价是最小的
所以我们只需要处理处理0101010..和10101010...的代价后直接取min就行
我最后代码还加了一个小小的剪枝通过看原序列0的个数是不是两种构造都成立,因为比如010和101它们0的个数是不一样的,如果原序列是110的话只能构造101了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
ll a[N];
ll pre[N],now1[N],now2[N];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
string s;
cin>>s;
//11100
//10101
string tmp1,tmp2;
for(int i=0;i<s.size();i++)
{
if(i%2==0)tmp1+='1',tmp2+='0';
else tmp1+='0',tmp2+='1';
}
ll cnt=0;
for(int i=0;i<s.size();i++)
{
if(s[i]=='0')
{
pre[++cnt]=i+1;
}
}
ll ans=0;
ll qnc=0;
for(int i=0;i<tmp1.size();i++)
{
if(tmp1[i]=='0')
{
now1[++ans]=i+1;
}
if(tmp2[i]=='0')
{
now2[++qnc]=i+1;
}
}
ll sum1=0;
ll sum2=0;
for(int i=0;i<tmp1.size();i++)
{
sum1+=abs(pre[i]-now1[i]);
}
for(int i=0;i<tmp2.size();i++)
{
sum2+=abs(pre[i]-now2[i]);
}
if(cnt==ans&&cnt!=qnc)
{
cout<<sum1<<endl;
}
else if(cnt!=ans&&cnt==qnc)
{
cout<<sum2<<endl;
}
else if(cnt==ans&&cnt==qnc)
{
cout<<min(sum1,sum2)<<endl;
}
}
D题
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
游游拿到了一个数字串,她想取一个该数字串的子序列(子序列在原串中可以不连续),使得该子序列是9的倍数。子序列可以包含前导零。
游游想知道,一共能取多少个合法的子序列?答案请对 109+7取模。
序列题一般都可以dp解决的
这里有个知识,序列如果是9的倍数的话那序列各个位置的数字之和的余数是0
我们可以设置dp[i][j]表示前i个数中余数是j的组合的个数
那怎么转移呢?
我们首先要预处理一下
for(int i=0;i<n;i++)
{
dp[i+1][(s[i]-'0')%9]++;
//先把每一个位置上的数的余数统计出来
}
我们知道单个数也可以构成序列的,所以第i个数的本身可能也是9的倍数这算一种
dp[i-1][j]是不算第i个字符的
dp[i-1][(9+j-(s[i]-'0'))%9]是算第i个字符的
转移的话就是这三个之和
因为我们是从小到大转移的所以在第i个数还没被更新的时候它原来的答案就是第一种(第一种我们已经先预处理过了)
(9+j-(s[i]-'0'))%9这个大家可能会不理解我这里解释一下:
如果算上第i个字符答案子序列和的余数是j的倍数,必须前i-1个字符的余数是j-(s[i]-'0')这样加上(s[i]-'0')时候余数才会是j
置于加9是因为可能会出现负数所以+9后外面再%9(这个技巧很常见)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
const int mod=1e9+7;
int dp[N][15];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
string s;
cin>>s;
int n=s.size();
for(int i=0;i<n;i++)
{
dp[i+1][(s[i]-'0')%9]++;
//先把每一个位置上的数的余数统计出来
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=8;j++)
{
dp[i][j] = (dp[i][j]+dp[i-1][j]+dp[i-1][(9+j-(s[i-1]-'0'))%9])%mod;
}
}
cout<<dp[n][0]%mod<<endl;
}