牛客周赛 Round 61

                                赛时AC:AB            rating: 1167               补题:ABCD


比赛链接牛客周赛 Round 61

目录

A

字符串处理

B

判断平方数要特别注意

平方数判断,等边三角形面积公式

C

组合数模版

计数取模特别提醒

二维平面移动可达性判断、组合数计数

D


A

输出十年后的今天,把字符串年转化为整型后+10;

但是我发现年分是2000-2024,不必这么麻烦。完全不用考虑逢十进一

虽然方法很多,但第一时间想到的方法都是好方法

字符串处理

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};
const ll N=2e5+5;
const ll mod=1e9+7;
ll a[N];
void solve()
{
	string s;
    cin>>s;
    string p=s.substr(0,4);
    ll ans=stoi(p);
    ans+=10;
    cout<<ans;
    for(int i=4;i<s.size();i++)
    {
        cout<<s[i];
    }
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	int t=1;//cin>>t;
	while(t--)
	{
		solve();
	}
}

B

首先判断正方形,即也就是判断一个数是不是平方数;

判断平方数要特别注意

先把sqrt转化为整型,切勿sqrt*sqrt。可恨的是,曾经我就发现了这个细节,结果后来却又在这跌倒了一次可怕的时不是跌倒,怕的是跌倒爬起来的再次跌倒。

考虑等腰三角形时,我没多想,后来看题解才发现,整数边长的面积结果都必不为整数,因为分子有一个根号三。

真实结果只有0和3.

等边三角形s=(a²√3)/4  (a为边长)

要判断也是可以,同时平方消去根号三,在开四次方根   回带验证即可。

平方数判断,等边三角形面积公式

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};
const ll N=2e5+5;
const ll mod=1e9+7;
ll a[N];
void solve()
{
	ll s;
    cin>>s;
    ll op=0,po=0;
    ll d=sqrt(s);
    ll p=s*s;
    p=p*16/3;
    ll c1=sqrt(p);
    c1=sqrt(c1);
    if(d*d==s)
    {
        op=1;
    }
    if(c1*c1*c1*c1==p)
    {
        po=1;
    }
    if(op==1)
    {
        cout<<"0\n";
    }
    else
    {
        cout<<3<<'\n';
    }

}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	int t=1;cin>>t;
	while(t--)
	{
		solve();
	}
}

C

赛时因为一些组合数计数的性质,没能正确计数(或多或少了)

其实之前也遇到过类似的,但一直未去搞懂。今天来仔细讲一讲。

其实这题有三个子问题,之前都遇到过类似的,都是典型


先来说

第一个问题

删除部分达到我们的目的,其实就是看最坏情况下能不能到。

也就是不走回头路,由于初始点在(0,0),也不必分四个象限讨论。

只需判断x轴或y轴最小值就可以,最小都可以的话,最大也就可以。

	ll n,x,y;
    cin>>n>>x>>y;
    ll cu=0,cd=0,cl=0,cr=0;
    string s;cin>>s;s=" "+s;
    for(int i=1;i<=n;i++)
    {
        if(s[i]=='U')
        {
            cu++;
        }
        if(s[i]=='D')
        {
            cd++;
        }
        if(s[i]=='L')
        {
            cl++;
        }
        if(s[i]=='R')
        {
            cr++;
        }
    }
    ll op=max(cu,cd);
    ll po=max(cl,cr);
    if(op>=abs(x)&&po>=abs(y))
    {
        cout<<"YES ";
    }
    else
    {
        cout<<"NO\n";
        return ;
    }

第二个问题

要求我们输出路径,能到的话,肯定是不走回头路这种最坏情况都能到(最短路)。

直接贪心  遇到我们需要的路就先走   输出就可以

    ll c[9]={0};
    c[1]=abs(x);c[2]=abs(x);c[3]=abs(y);c[4]=abs(y);
    ll f=0;
        for(int i=1;i<=n;i++)
        {
            if(c[1]&&s[i]=='U'&&x>0)
            {
                cout<<"U";
                c[1]--;
            }
            else if(c[2]&&s[i]=='D'&&x<0)
            {
                cout<<"D";
                c[2]--;
            }
            else if(c[3]&&s[i]=='L'&&y<0)
            {
                cout<<"L";
                c[3]--;
            }
            else if(c[4]&&s[i]=='R'&&y>0)
            {
                cout<<"R";
                c[4]--;
            }
        }

第三个问题

接下来是本题重点研究的部分

由于本次研究重点不是组合数,而是关于组合数的计数,因此求组合数代码直接给出模版

组合数模版

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};
const ll N=1e5+5;
const ll p=1e9+7;
ll a[N];
ll jc[N+5],inv[N+5],pw[N+5];
ll ksm(ll a,ll b)
{
	ll ans=1;
	a%=p;
	while(b)
	{
		if(b&1)
		{
			ans=ans*a%p;//错过一次 
		}
		a=a*a%p;
		b>>=1;
	}
	return ans;
}
void  init()
{
	jc[0]=inv[0]=pw[0]=1;
	for(int i=1;i<=N;i++)
	{
		jc[i]=jc[i-1]*i%p;
	}
	inv[N]=ksm(jc[N],p-2);
	for(int i=N-1;i>=1;i--)
	{
		inv[i]=inv[i+1]*(i+1)%p;
	}
} 
ll C(ll n,ll m)//cnm
{
	if(n<m||n<0||m<0)
	{
		return 0;
	}
	return jc[n]*inv[m]%p*inv[n-m]%p;//inv[m]是m!取模p的逆元 
}

计数分析

其实在水平方向,沿同一方向有x个该方向是必选的。

其实在垂直方向,沿同一方向有y个该方向是必选的。

当我们这个方向多选一个,我们就要选一个反方向的一个,此时是一种情况,但实现该情况是有多个操作方法的。

分四种情况讨论就行了

计数取模特别提醒

加过后再单独取模

要下面这样

ret+=C(cr,i-y)*C(cl,i);
            ret%=p;

而不是这样

ret+=C(cr,i-y)*C(cl,i)%p;

ll res=0,ret=0;
    if(x>=0)
    {
        for(int i=x;i<=cu;i++)
        {
            if(i-x>cd)
                break;
            res+=C(cu,i)*C(cd,i-x);
            res%=p;
        }
    }
    else
    {
        x=-x;
        for(int i=x;i<=cd;i++)
        {
            if(i-x>cu)
                break;
            res+=C(cu,i-x)*C(cd,i);
            res%=p;
        }
    }
    //cout<<res;
        if(y>=0)
    {
        for(int i=y;i<=cr;i++)
        {
            if(i-y>cl)
                break;
            ret+=C(cr,i)*C(cl,i-y);
            ret%=p;
        }
    }
    else
    {
        y=-y;
        for(int i=y;i<=cl;i++)
        {
            if(i-y>cr)
                break;
            ret+=C(cr,i-y)*C(cl,i);
            ret%=p;
        }
    }
    ll ans=res%p*ret%p;
    cout<<ans;
    cout<<"\n";

二维平面移动可达性判断、组合数计数

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};
const ll N=1e5+5;
const ll p=1e9+7;
ll a[N];
ll jc[N+5],inv[N+5],pw[N+5];
ll ksm(ll a,ll b)
{
	ll ans=1;
	a%=p;
	while(b)
	{
		if(b&1)
		{
			ans=ans*a%p;//错过一次 
		}
		a=a*a%p;
		b>>=1;
	}
	return ans;
}
void  init()
{
	jc[0]=inv[0]=pw[0]=1;
	for(int i=1;i<=N;i++)
	{
		jc[i]=jc[i-1]*i%p;
	}
	inv[N]=ksm(jc[N],p-2);
	for(int i=N-1;i>=1;i--)
	{
		inv[i]=inv[i+1]*(i+1)%p;
	}
} 
ll C(ll n,ll m)//cnm
{
	if(n<m||n<0||m<0)
	{
		return 0;
	}
	return jc[n]*inv[m]%p*inv[n-m]%p;//inv[m]是m!取模p的逆元 
}
void solve()
{
	ll n,x,y;
    cin>>n>>x>>y;
    ll cu=0,cd=0,cl=0,cr=0;
    string s;cin>>s;s=" "+s;
    for(int i=1;i<=n;i++)
    {
        if(s[i]=='U')
        {
            cu++;
        }
        if(s[i]=='D')
        {
            cd++;
        }
        if(s[i]=='L')
        {
            cl++;
        }
        if(s[i]=='R')
        {
            cr++;
        }
    }
    ll op=max(cu,cd);
    ll po=max(cl,cr);
    if(op>=abs(x)&&po>=abs(y))
    {
        cout<<"YES ";
    }
    else
    {
        cout<<"NO\n";
        return ;
    }
    init();
    ll c[9]={0};
    c[1]=abs(x);c[2]=abs(x);c[3]=abs(y);c[4]=abs(y);
    ll f=0;
        for(int i=1;i<=n;i++)
        {
            if(c[1]&&s[i]=='U'&&x>0)
            {
                cout<<"U";
                c[1]--;
            }
            else if(c[2]&&s[i]=='D'&&x<0)
            {
                cout<<"D";
                c[2]--;
            }
            else if(c[3]&&s[i]=='L'&&y<0)
            {
                cout<<"L";
                c[3]--;
            }
            else if(c[4]&&s[i]=='R'&&y>0)
            {
                cout<<"R";
                c[4]--;
            }
        }
        cout<<" ";
    ll res=0,ret=0;
    if(x>=0)
    {
        for(int i=x;i<=cu;i++)
        {
            if(i-x>cd)
                break;
            res+=C(cu,i)*C(cd,i-x);
            res%=p;
        }
    }
    else
    {
        x=-x;
        for(int i=x;i<=cd;i++)
        {
            if(i-x>cu)
                break;
            res+=C(cu,i-x)*C(cd,i);
            res%=p;
        }
    }
    //cout<<res;
        if(y>=0)
    {
        for(int i=y;i<=cr;i++)
        {
            if(i-y>cl)
                break;
            ret+=C(cr,i)*C(cl,i-y);
            ret%=p;
        }
    }
    else
    {
        y=-y;
        for(int i=y;i<=cl;i++)
        {
            if(i-y>cr)
                break;
            ret+=C(cr,i-y)*C(cl,i);
            ret%=p;
        }
    }
    ll ans=res%p*ret%p;
    cout<<ans;
    cout<<"\n";
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	int t=1;cin>>t;
	while(t--)
	{
		solve();
	}
}

D

由于原数组是单调递增的,不难想到和中间那个数有关系

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值