2021牛客暑期多校训练营1
题目按照通过率排序
D Determine the Photo Position
地址:Determine the Photo Position 签到题
题意:有一个n行m列的01矩阵和一个由2组成的字符串,问用该字符串替换矩阵中的0字符串有多少种方案
题解:因为字符串只有一行,所以只需要计算字符串在矩阵每一行可以替换几次。在一每行中只需要遍历一遍寻找有多少与2字符串长度相同的0字符串即可
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn= 2e4+5;
char mp[maxn][maxn],str[maxn];
void solve(){
int n,m,ans=0;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
scanf("%s",mp[i]+1);
int num=0;
for(int j=1;j<=n+1;j++){
if(mp[i][j]=='0')num++;
else{
ans+=max(0,num-m+1);
num=0;
}
}
}
cout<<ans<<endl;
for(int i=1;i<=n;i++) cin>>str;
}
int main(int argc,char *argv[]){
solve();
return 0;
}
B Ball Dropping
题意:已知r,a,b,h(1≤r,a,b,h≤1000,a>b) 计算图中红线的长度
题解:如图做出3条辅助线,由于平行线∠1=∠2,发现许多相似直角三角形,利用边成比例求得答案
代码:
#include<bits/stdc++.h>
using namespace std;
void solve(){
double r,a,b,h;
cin>>r>>a>>b>>h;
if(b>2*r) cout<<"Drop\n";
else{
cout<<"Stuck\n";
double ans=(r*sqrt(4*h*h+(b-a)*(b-a))-b*h)/(a-b);
printf("%.10lf\n",ans);
}
}
int main(int argc,char *argv[]){
solve();
return 0;
}
F Find 3-friendly Integers
地址: Find 3-friendly Integers 思维题
题意:规定3的友好数x当且仅当 x%3=0或 x连续的子串的十进制表示d%3=0 ,求l到r之间有多少个3的友好数
题解:数位DP(可以但没必要)。思维:当x为三位数及以上是x一定是3的友好数,对于两位及一位x暴力打表即可
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll a[105];
void init(){
int num=0;
for(int i=1;i<=100;i++){
if(i<10)
if(i%3==0) num++;
if(i>=10)
if(i%3==0||(i/10)%3==0||(i%10)%3==0) num++;
a[i]=num;
}
}
void solve(){
ll l,r;
ll ans=0;
scanf("%lld%lld",&l,&r);
if(l>=100) cout<<r-l+1<<endl;
else {
ll x = 1;
ans+=max(x*0,r-100+1);
r=min(99*x,r);
ans+=a[r]-a[l-1];
cout<<ans<<endl;
}
}
int main(int argc,char *argv[]){
init();
int _;cin>>_;
while(_--)
solve();
return 0;
}
G Game of Swapping Numbers
题意:给出两个数组a,b,能对a中元素交换k次,求最大的sum(|Ai-Bi|)
题解:先计算不交换的情况下答案是多少,再把ai与bi中较大者放入一个数组c,较小值放入另一个数组d,第i次交换相当于答案加上c中第i大值与d中第i小值的差值,前提是差为正数
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+5;
int a[maxn],b[maxn];
bool cmp(int x,int y){
return x>y;
}
int main(){
long long n,m,ans=0,k=0;
cin>>n>>m;m=min(n,m);
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
cin>>b[i];
if(a[i]>b[i]) swap(a[i],b[i]);
ans+=b[i]-a[i];
}
sort(1+a,1+a+n,cmp);
sort(1+b,1+b+n);
for(int i=1;i<=m;i++){
k=a[i]-b[i];
if(k>0) ans+=k*2;
}
cout<<ans<<endl;
}
I Increasing Subsequence
地址: Increasing Subsequence 概率DP
题意:给出排列P,两个人轮流取数,每次取的数需要在之前该人取数的右边,且比当前取出来所有的数都要大。所有当前可选的数都将等概率随机的被当前决策人选中。问两个人期望取数的轮数。
题解:设dp[i] [j]表示当前选i上一次选j时的概率和,假设下一次有cnt个选择,我们选择了k,由于每个选择概率相等,所以状态转移方程为dp[i] [j]=1+1/cnt * dp[j] [k] (k>y,P[k]>P[x],P[k]>P[y]),可以看出每次当前状态与之后状态有关,所以实现时逆序求DP
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=5005,mod=998244353;
typedef long long LL;
const double eps=1e-7;
LL a[N],inv[N];
LL n;
LL f[N][N];
int main(){
cin>>n;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
inv[1]=1;for(int i=2;i<=n;i++)inv[i]=(LL)inv[M%i]*(M-M/i)%M;
//记住,这里是往后递推,类似拓扑图
//逆着写期望DP,这里发现,每一次与上一次的值有关,下标逆着推能够很好的处理游戏轮数
for(int j=n;j>=0;j--)
{//这次选择J,从大到小
LL cnt=0,sum=0;//期望和,也就是这次选择的数大于J,所有期望和,cnt,均等概率
for(int i=n;i>=0;i--){// 下一次选择的a[i]
if(a[i]>j)//说明这里已经计算过了
{
cnt++;//往前做的时候可以累加大于 J 的下标个数
sum+=f[j][a[i]];//所有期望和
sum%=mod;
}
else if(a[i]<j){//下一次不可能选择a[i],所以这一次选择a[i],下一次选择J能够更新,
//这里上一次是J,这一次是a[i]的方案根本不存在
f[a[i]][j]=1;//这里期望必有 1 ,就是转移到这个状态有一轮,不管这人是谁,选择的是a[i],游戏轮数是1,能否被其他状态转移过来呢?
if(!cnt)continue;
f[a[i]][j]+=sum*inv[cnt]%mod;//加上后面他可以转移的,遵循均等的原则
f[a[i]][j]%=mod;
}
}
}
LL res=0;
for(int i=1;i<=n;i++) res=(res+f[0][i])%mod;
//最后第一个也是均等选择的
res=res*inv[n]%mod;
cout<<res<<endl;
return 0;
}
A Alice and Bob
地址: Alice and Bob 博弈
题意: 两人博弈,每次一个人从一堆中拿 k 个,同时从另一堆拿 k * s(s >= 0) 个,问谁先不能拿。
题解:队友博弈选手说直接打表再输出就ok
代码:等队友补充
H: Hash Function
题意: 给定n个互不相同的数,找一个最小的数,使得它们对这个数取模互不相同
题解:赛时因为数据较弱莫名其妙的过了,赛后才知道正解是卷积利用FFT加速。
先根据模运算的一个性质 若 a ≡ b mod p,则a−b ≡ 0 mod p ,将问题转换为 求最小的、且不为所给的数中任意两个数的差的因数的数。 朴素算法需要n^2,可以考虑多项式优化为nlogn。
一个多项式系数为xi表示数i是否出现在数组中。
另一个多项式系数yi表示数-i是否出现在数组中。但是,多项式的项数不能为负数呀?那么,考虑给它+5e5,使得意义为5e5-i这个数是否在数组中出现。
两个多项式用FFT相乘后,卷积的结果即为:第i项表示是否有i+5e5-j这个数存在于这个数组中任选两个数相减后的结果,因此可知,下标小于5e5的部分没有意义。若某一项系数xi>=1,则说明i-5e5这个数可以差出来。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
typedef complex<double> CP;
const int lim = 1<<21;
const double Pi = acos(-1);
CP a[lim],b[lim];
void FFT(CP *x,int lim,int inv)
{
int bit = 1,m;
CP stand,now,temp;
while((1<<bit) < lim) ++bit;
for (int i = 0; i < lim; ++i)
{
m = 0;
for (int j = 0; j < bit; ++j)
if(i & (1<<j)) m |= (1<<(bit-j-1));
if(i < m) swap(x[m],x[i]);
}
for (int len = 2; len <= lim; len <<= 1)
{
m = len >> 1;
stand = CP(cos(2*Pi/len),inv*sin(2*Pi/len));
for (CP *p = x; p != x+lim; p += len)
{
now = CP(1,0);
for (int i = 0; i < m; ++i,now*=stand)
{
temp = now * p[i+m];
p[i+m] = p[i] - temp;
p[i] = p[i] + temp;
}
}
}
if(inv == -1)
for (int i = 0; i < lim; ++i)
x[i].real(x[i].real()/lim);
}
int P=500000;
int vis[1<<21];
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
int x;
cin>>x;
a[x].real(1);
b[P-x].real(1);
}
int len = 1<<20;
FFT(a,len,1);
FFT(b,len,1);
for(int i = 0;i < len;i++)
a[i] = (a[i]*b[i]);
FFT(a,len,-1);
for(int i = 0;i <= len; i++) {
int tmp = a[i].real()+0.5;
if (tmp > 0) vis[abs(i-P)] = 1;
}
for (int i = n; i <= P; i++) {
bool flag = 1;
for (int j = i; j <= P; j += i) {
if (vis[j] == 1) {
flag = 0;
break;
}
}
if (flag) {
cout << i << endl;
break;
}
}
return 0;
}
Knowledge Test about Match
地址: Knowledge Test about Match 贪心
题意:随机生成一个权值范围为 0~n-1 的序列a和b,你要交换b中的元素 去匹配a,匹配函数是 sqrt。 要求平均情况下和标准值偏差不能超过 4%。
题解:跟据sqrt函数图像可以发现,当元素个数较少时ai与bi差值对结果影响较大,所以我们只需要贪心先匹配差值为0的元素,再匹配差值为1的元素…
直接用sort排序后匹配是不行的,举一个例子:{1,2,3}, {0,1,2},(1,1), (2,2), (0, 3) 会比 sort 的结果好很多。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int a[maxn],vis[maxn];
void solve(){
int n,x;
cin>>n;
for(int i=0;i<n;i++) a[i]=-1,vis[i]=0;
for(int i=0;i<n;i++){
cin>>x;
vis[x]++;
}
for(int dis=0;dis<n;dis++){
for(int i=0;i<n;i++){
if(a[i]==-1){
if(i-dis>=0&&vis[i-dis]){
a[i]=i-dis;
vis[i-dis]--;
}
else if(i+dis<n&&vis[i+dis]){
a[i]=i+dis;
vis[i+dis]--;
}
}
}
}
for(int i=0;i<n;i++) cout<<a[i]<<" ";cout<<endl;
}
int main() {
int _;cin>>_;
while(_--)
solve();
return 0;
}