2020牛客寒假算法基础集训营4

姗姗来迟;
异或那道题没想出来可惜了;

官方标程:标程

A:欧几里得

打标找规律,题解说的斐波那契;

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
struct node
{
    ll a,b;
}bb[100];
int main()
{
    int t;
    cin >>t;
    bb[1].a=2,bb[1].b=1;
    for(int i=2;i<=80;i++)
    {
        bb[i].a=bb[i-1].a+bb[i-1].b;
        bb[i].b=bb[i-1].a;
    }
    while(t--)
    {
        int n;
        cin >>n;
        if(n==0) cout <<1<<endl;
        else cout <<bb[n].a+bb[n].b<<endl;
    }
 }

.
.

B: 括号序列

思路是把向左的括号都放到栈里面,然后遇到向右的括号,就把它与栈顶的括号相匹配,匹配失败则不符合题意;
(最后一定要判断栈是否为空,只有栈是空的,说明才符合题意)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
map<char,char> mp;
int main()
{
    mp[')']='(';
    mp['}']='{';
    mp[']']='[';
    string s;
    cin >>s;
    int len=s.size();
    stack <char> ss;
    int flag=1;
    for(int i=0;i<len;i++)
    {
        if(s[i]=='('||s[i]=='['||s[i]=='{')
        {
            ss.push(s[i]);
        }
        else
        {
            if(ss.empty())
            {
                flag=0;
                break;
            }
            else
            {
                if(ss.top()==mp[s[i]]) ss.pop();
                else
                {
                    flag=0;
                    break;
                }
            }
        }
    }
    if(!ss.empty()) flag=0;
    if(flag) cout <<"Yes"<<endl;
    else cout <<"No"<<endl;
}

.
.

C: 子段乘积

先求前缀乘积,然后区间【l,r】的乘积为mul [ r ] / mul [ l ] ;需要用到逆元;
并且需要判断这个子段中是否有0,快速的判断方法为,用前缀和处理原始数据,如果输入的是0,就标记为1,不是0,标记为0;

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=998244353;
ll a[2*100005];
int b[2*100005];
int cnt,n,k;
ll qk(ll a,ll b)
{
    ll res=1;
    while(b)
    {
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
int main()
{
 
    cin >>n>>k;
    a[0]=1;
    for(int i=1;i<=n;i++)
    {
        cin >>a[i];
        if(a[i]==0) b[i]=b[i-1]+1,a[i]=a[i-1];
        else
        {
            a[i]=a[i]*a[i-1]%mod;
            b[i]=b[i-1];
        }
    }
    ll ans=0;
    for(int i=k;i<=n;i++)
    {
        if(b[i]-b[i-k]) continue;
        ans=max(ans,a[i]*qk(a[i-k],mod-2)%mod);
    }
    cout <<ans%mod<<endl;
}

.
.

D:子段异或

这个题非常有意思,异或零的话是:x ^ x = 0 ;所以求出前缀和,当出现两次前缀和一样时,说明中间一定有一段子串的异或为0;如果该前缀和出现三次,那么说明这一次到第一次中间有两端子串的异或值为0;那么这两串可以组成三个不同的串;以此类推。。。。。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
map<ll,int> mp; 
ll a[2000005];
int main()
{
	int n;
	cin >>n;
	mp[0]=1;
	ll ans=0,res=0;
	for(int i=0;i<n;i++)
	{
		ll a[i];
		cin >>a[i];
		res^=a[i];
		ans+=mp[res];
		mp[res]++;
	}
	cout <<ans<<endl;
}

.
.
.

E.最小表达式

该题把数列从小到大排序后,如果有x个加号,那么把数列里面的数依次分给这(x+1)个子数列;再把这(x+1)个数列组成的数,相加即可;(大数运算)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[5*100001];
vector<int> add(vector<int> &A, vector<int> &B)
{
    if (A.size() < B.size()) return add(B, A);
    vector<int> C;
    int t = 0;
    for (int i = 0; i < A.size(); i ++ )
    {
        t += A[i];
        if (i < B.size()) t += B[i];
        C.push_back(t % 10);
        t /= 10;
    }
    if (t) C.push_back(t);
    return C;
}
int main()
{
	string s;
	cin >>s;
	int len=s.size();
	int cnt=0,cot=0;
	for(int i=0;i<len;i++)
	{
		if(s[i]=='+')  cot++;
		else a[cnt++]=s[i]-'0';
	}
	sort(a,a+cnt);
	vector<int> v[cot+1];
	for(int i=0;i<=cot;i++)
	{
		stack<int> q;
		for(int j=i;j<cnt;j+=(cot+1))
			q.push(a[j]);
		while(!q.empty()) v[i].push_back(q.top()),q.pop();
	}
//	for(int i=0;i<=cot;i++)
//	{
//		for(int j=0;j<v[i].size();j++) cout <<v[i][j];
//		cout <<endl;
//	}
	vector<int> ans;
	for(int i=0;i<v[0].size();i++) ans.push_back(v[0][i]);
	for(int i=1;i<=cot;i++) ans=add(ans,v[i]);
	for(int i=ans.size()-1;i>=0;i--) cout <<ans[i];
}

.
.
.

F:树上博弈

只有牛牛的初始位置和牛妹的初始位置距离为偶数时,牛牛获胜。只需要分别求出深度为奇数的点和深度为偶数的点的数量即可。

#include <bits/stdc++.h> 
using namespace std;
typedef long long ll;
int dp[1000005];
ll cnt[2];

int main() {
    int n;
    cin >>n;
    dp[1] = 0;
    cnt[0] = 1;
    for(int i=2;i<=n;i++){
        int p;
        cin >>p;
        dp[i] = dp[p]^1;
        cnt[dp[i]]+=1;
    }
    cout<<cnt[0]*(cnt[0]-1)+cnt[1]*(cnt[1]-1)<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值