牛客周赛 Round 68(A~E)
比赛链接:https://ac.nowcoder.com/acm/contest/95928#question
这次D题小细节搞了好久,越界了好几次,没想到赛后做E,发现还更简单
A.三途川的摆渡人(二)
题面:
小红这天来到了三途川,发现这里有一个摆渡人,名字叫小町。
小町的职责是将一些灵魂运送到冥界。但小町非常喜欢偷懒,她经常在上班的时候摸鱼,导致很多灵魂不能成功送达,因此在彼岸开出了许多彼岸花。
小红准备统计彼岸花的数量,她把排队等待摆渡的灵魂用一个01串来表示,'0'代表因小町摸鱼未能上船的灵魂,'1'代表成功到达冥界的灵魂。每个未上船的灵魂都将化为一朵彼岸花,请你帮小红计算彼岸花的数量。
输入:
第一行输入一个正整数n,代表灵魂的数量。
第二行输入一个长度为n的01串,代表每个灵魂是否成功被小町送往冥界。
\(1<=n<=100\)
输出:
一个整数,代表彼岸花的数量。
样例:
4
0100
——————
3
思路:彼岸花数量就是字符串中0数量,直接遍历
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
int main()
{
fio();
ll n;
cin>>n;
string f;
cin>>f;
ll ans=0;
for(ll i=0;i<f.size();i++)
{
if(f[i]=='0')ans++;
}
cout<<ans<<endl;
}
B.魔法之森的蘑菇(二)
题面:
小红在魔法之森迷路了,森林中有一些致幻的毒蘑菇。森林用一个𝑛行𝑚列的矩阵表示。
小红准备采集一个矩形区域里的资源,但小红非常讨厌蘑菇,因此她希望这个矩形内没有任何蘑菇。
小红希望你帮她计算出一个面积最大的、且不包含蘑菇的矩形区域。
输入:
第一行输入两个正整数𝑛,𝑚代表矩阵的行数和列数。
接下来的𝑛行,每行输入一个长度为𝑚的字符串,用来表示森林地图。
保证所有的字符仅有'.'和''这两种,其中'.'代表道路,''代表蘑菇
\(1<=n,m<=30\)
输出:
四个整数$ x_1$, \(y_1\), \(x_2\), \(y_2\),用空格隔开。代表矩形的左上角在第$ x_1 $行第 $ y_1 $ 列,右下角在第 \(x_2\) 行第 \(y_2\) 列。保证至少有一个合法的答案。
样例:
3 3
\(.*.\)
\(...\)
\(.*.\)
——————
1 1 3 1
思路:二维前缀和+暴力枚举,首先用二维前缀处理好所*的个数,然后暴力&n^4&枚举左上角和右下角,随后每次减一下检测一下是否全为0即可,然后再比较下当前面积和记录的最大面积。如果能更新就更新,然后更新2个点的坐标
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll pre[50][50];
int main()
{
fio();
ll n,m;
cin>>n>>m;
string f[50];
for(ll i=1;i<=n;i++)cin>>f[i],f[i]='0'+f[i];
for(ll i=1;i<=n;i++)
{
for(ll j=1;j<=m;j++)
{
pre[i][j]=pre[i][j-1]+pre[i-1][j]-pre[i-1][j-1]+(f[i][j]=='*');
}
}
ll ans=0;
ll l,r,l1,r1;
for(ll i=1;i<=n;i++)
{
for(ll j=1;j<=m;j++)
{
for(ll k=i;k<=n;k++)
{
for(ll c=j;c<=m;c++)
{
// if(k==3&&c==3&&i==3&&j==1)
// {
// cout<<pre[k][c]-pre[k][j-1]-pre[i-1][c]+pre[i-1][j-1]<<endl;
// }
if(pre[k][c]-pre[k][j-1]-pre[i-1][c]+pre[i-1][j-1]==0)
{
if(ans<(c-j+1)*(k-i+1))
{
ans=(c-j+1)*(k-i+1);
l=i,r=j,
l1=k,r1=c;
}
}
}
}
}
}
cout<<l<<" "<<r<<" "<<l1<<" "<<r1<<endl;
}
C.迷途之家的大贤者(二)
题面:
小红在穿越后不久,就被大贤者小紫发现了,于是小紫友好地请小红来迷途之家做客。
她们准备玩一个游戏,两人分别拿到了一个长度为 $n $ 的数组。她们可以进行若干次操作。
对于每次操作,两人可以同时删除自己数组中任意一个元素。她们希望两人通过若干次删除操作,使得操作后的两个数组中所有元素均互不相同。例如 \([1,2][3,4]\) 满足条件,而 \([1,2][2,3]\) 不满足条件,因为两个数组中有两个值为2的元素。
你能帮小红求出操作的最小次数吗?
输入:
第一行输入一个正整数 $n $,代表每个人数组的大小。
第二行输入 $ n $ 个正整数 $ a_i $,代表小红拿到的数组元素。
第三行输入 $ n $ 个正整数 $ b_i $,代表小紫拿到的数组元素。
$ 1 \leq n \leq 10^5 $
$ 1 \leq a_i, b_i \leq 10^9 $
输出:
一个整数,代表操作的最小次数。
样例:
4
1 2 1 3
2 4 3 5
————
2
思路:首先思考下,发现删除自己重复的数是最优先的,然后再删除和别人重复的是其次的,所以通过这种方法可以讨论,先分别记录两个数组的删自己重复数的次数:cnt,cnt1
如果cnt==cnt1
则剩下的数一定是重复的且其值为1,所以答案为(重复的数的种类+1)/2+cnt
如果cnt>cnt1
首先则剩下的两者重复的数,后者可以先删除cnt-cnt1个,随后答案为(重复的数的种类-(cnt-cnt1)+1)/2+cnt
否则反之
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll a[500000];
ll b[500000];
int main()
{
fio();
ll n;
cin>>n;
map<ll,ll>q1,q2;
set<ll>f;
for(ll i=1;i<=n;i++)
{
cin>>a[i];
q1[a[i]]++;
f.insert(a[i]);
}
for(ll i=1;i<=n;i++)cin>>b[i],q2[b[i]]++,f.insert(b[i]);
ll ans=0;
ll cnt=0;
ll cnt1=0;
for(auto j:f)
{
if(q1[j]>0)
cnt+=q1[j]-1;
if(q2[j]>0)
cnt1+=q2[j]-1;
}
//ll ans=0;
if(cnt==cnt1)
{
ll cs=0;
for(auto j:f)
{
if(q1[j])q1[j]=1;
if(q2[j])q2[j]=1;
if(q1[j]&&q2[j])cs++;
}
cout<<(cs+1)/2+cnt<<endl;
}
else if(cnt>cnt1)
{
ll co=cnt-cnt1;
ll cs=0;
for(auto j:f)
{
if(q1[j])q1[j]=1;
if(q2[j])q2[j]=1;
if(q1[j]&&q2[j])
{
if(co)
{
co--;
continue;
}
else
{
cs++;
}
}
}
cout<<(cs+1)/2+cnt<<endl;
}
else
{
swap(cnt,cnt1);
ll co=cnt-cnt1;
ll cs=0;
for(auto j:f)
{
if(q1[j])q1[j]=1;
if(q2[j])q2[j]=1;
if(q1[j]&&q2[j])
{
if(co)
{
co--;
continue;
}
else
{
cs++;
}
}
}
cout<<(cs+1)/2+cnt<<endl;
}
}
D.红魔馆的馆主(二)
题面:
小红来到了红魔馆。众所周知,红魔馆的馆主是一只495岁的吸血鬼,所以她非常喜欢495这个数。
现在小红拿到了一个数组,她认为该数组的“美丽度”为:选两个元素 $a_i $ 和 $a_j (i<j) $,乘积为495的倍数的方案数。
小红可以进行最多1次操作:选择一个元素,使其加1。请你帮小红求出最多1次操作后,数组的最大美丽度。
输入:
第一行输入一个正整数 $ n $,代表每个人数组的大小。
第二行输入 $ n $ 个正整数 $ a_i $,代表数组的元素。
$ 1 \leq n, a_i \leq 400000 $
输出:
一个整数,代表最多一次操作后,数组的最大美丽度。
样例:
4
1 9 55 494
————
4
思路:从495的因数出发,显然一个数存有495因数,那么他乘以其他带有495因数的数结果%495==0,就等价于他们相乘是为495的倍数。
所以先用个数组存495的所有因数可能,然从大到小排序,去重。总共12种可能
然后对于每个数标记它属于哪个可能,即第一次被模除的数的存储下标
用前缀数组和后缀数组,存储和所有状态并传递
首先对于不操作的答案,直接用后缀数组+判断它和其他11种组合是否构成495倍数可以计算出
然后暴力枚举,每个数加+1的情况,首先用前后缀数组把它所有可能都减掉,然后加1,再判断它属于哪种可能
,再次用前后缀数组把他所用可能都加起来,最后取个max
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll a[500000];
ll pre[14][500000];
ll sub[14][500000];
ll d[500000];
int main()
{
fio();
ll n;
cin>>n;
memset(d,0,sizeof d);
ll b[60]={0,495,165,99,45,55,33,15,9,11,5,3,1};
for(ll i=1;i<=n;i++)
{
cin>>a[i];
ll u=a[i];
for(ll k=1;k<=12;k++)
{
pre[k][i]=pre[k][i-1];
}
for(ll j=1;j<=12;j++)
{
if(a[i]%b[j]==0)
{
d[i]=j;
pre[j][i]++;
break;
}
}
}
for(ll i=n;i>=1;i--)
{
for(ll k=1;k<=12;k++)
{
sub[k][i]=sub[k][i+1];
}
for(ll j=1;j<=12;j++)
{
if(a[i]%b[j]==0)
{
sub[j][i]++;
break;
}
}
}
ll ans=0;
for(ll i=1;i<=n;i++)
{
for(ll j=1;j<=12;j++)
{
if((b[j]*b[d[i]])%495==0)
{
ans+=sub[j][i+1];
}
}
}
ll u=ans;
//cout<<u<<endl;
ll cnt;
for(ll i=1;i<=n;i++)
{
cnt=u;
for(ll j=1;j<=12;j++)
{
if((b[j]*b[d[i]])%495==0)
{
// if(i==1)
// cout<<j<<endl;
cnt-=pre[j][i-1]+sub[j][i+1];
}
}
a[i]++;
ll wz=0;
for(ll j=1;j<=12;j++)
{
if(a[i]%b[j]==0)
{
wz=j;
break;
}
}
for(ll j=1;j<=12;j++)
{
if((b[j]*b[wz])%495==0)
{
cnt+=pre[j][i-1]+sub[j][i+1];
}
}
ans=max(ans,cnt);
}
cout<<ans<<endl;
}
E.博丽神社的巫女(二)
题面:
小红来到了博丽神社,发现博丽神社门口有一个赛钱箱。
这时神社里走出了一个巫女,对小红说:给你一个数组,你可以对任意元素进行任意次“该元素除以2向下取整”的操作,只要你能让这个数组所有元素之和等于100000,你就能拿走赛钱箱里所有的钱,否则你就必须往赛钱箱投十万元,你想和我玩这个游戏吗?
小红很想赚取赛钱箱里的金币,请你帮帮小红。
输入:
第一行输入一个正整数 $ n $ ,代表巫女给小红的数组大小。
第二行输入 $ n $ 个正整数 $ a_i $ ,代表数组的元素。
$ 1 \leq n \leq 100 $
$ 1 \leq a_i \leq 10^5 $
保证所有元素之和 $ S $ 的范围满足 $ 100000 < S < 200000 $
输出:
如果小红必然输掉游戏,请输出-1。
否则输出 $ n $ 个非负整数 $ b_i (0 \leq b_i \leq 10^5) $,分别代表对第 $ i $ 个元素执行 $ b_i $ 次操作。
样例:
2
50000 100000
————
0 1
思路:分组背包变形+set转移数组。首先加和达不到100000直接判-1。
然后考虑如何变形,把原本凑100000,变成是否能减sum-100000,随后这个差值用分组背包变形就好,除了0其他都得复制为一个极大值
首先预处理一个数的所有可能差值与操作次数,vector记住,随后就是分组背包跑一遍,记得set开pair记录操作次数和对谁操作
最后判断dp[sum-100000]是否仍为极大值,如果是,输出-1,否则输出答案
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll a[500];
vector<pair<ll,ll>>g[250];
set<pair<ll,ll>>q[200001];
ll dp[1500000];
ll b[250000];
int main()
{
fio();
ll n;
cin>>n;
ll sum=0;
for(ll i=1;i<=n;i++)cin>>a[i],sum+=a[i];
for(ll i=1;i<=n;i++)
{
ll cs=0;
ll u=a[i];
while(u)
{
u/=2;
cs++;
g[i].push_back({cs,a[i]-u});
}
}
if(sum<100000)
{
cout<<-1<<endl;
}
else
{
sum-=100000;
for(ll i=1;i<=sum;i++)
{
dp[i]=9999999999;
}
for(ll i=1;i<=n;i++)
{
for(ll j=sum;j>=1;j--)
{
for(auto k:g[i])
{
ll u=k.first;
ll f=k.second;
if(j>=f)
{
if(dp[j]>dp[j-f]+1)
{
dp[j]=dp[j-f]+1;
q[j].clear();
q[j].insert({u,i});
q[j].insert(q[j-f].begin(),q[j-f].end());
}
}
}
}
}
if(dp[sum]==9999999999)cout<<-1<<endl;
else
{
for(auto j:q[sum])
{
b[j.second]=j.first;
}
for(ll i=1;i<=n;i++)cout<<b[i]<<" ";
cout<<endl;
}
}
}