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

地址: 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

地址: 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

地址: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; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

温柔说给风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值