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

A 欧几里得
求递归n次 a+b最小的和
def gcd(a,b):
if b == 0:
return a
return gcd(b,a%b)
观察函数会发现 第n层可以从第n-1层返回来 第n层的(a,b)等于n-1层的(b,a%b)
所以n-1层的a1,b1和n层的a,b的关系是b=a1 ,b1=a%b;b的值已经确定要使和最小 就要使a最小
就是a=a1+b1,需要注意0层的b=0,这时候要使a+1。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    int t,n;
    cin>>t;
    while(t--)
    {
        cin>>n;
        ll a=1,b=0;
        while(n--)
        {
            if(b==0) b=1;
            ll x=b;
            b=a;
            a=a+x;
        }
        cout<<a+b<<endl;;
    }
    return 0;
}

B 判断括号是否合法是栈的基础应用

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
stack<char> st;
int main()
{
    string s;
    cin>>s;
    for(int i=0;i<s.size();i++)
    {
        if(st.size()==0) st.push(s[i]);
        else if(st.top()=='('&&s[i]==')') st.pop();
        else if(st.top()=='['&&s[i]==']') st.pop();
        else if (st.top()=='{'&&s[i]=='}') st.pop();
        else st.push(s[i]);
    }
    if(st.size()==0) puts("Yes");
    else puts("No");
    return 0;
}

C 求长度为k的子段最大乘积可以使用前缀和,用长度为k的区间平移求最大值就可以,因为要用前缀的积除另一个前缀的积而且要取模 就得用到逆元了,并且模数是一个素数用费马小定理比较好算,如果模数不是素数就必须要用拓展欧几里得了 还有一点需要注意如果子段中有0就不满足题意了,需要把有零的子段删去。那怎么判断呢,我用了一个前缀和,把非零的数当做1,0还是0求一遍前缀和 如果子段和不等于k 那么子段中一定有0 continue就可以了

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

D 异或结果等0需要两个数相等
打表可以发现如果区间[l,r]异或等0,就会有[1,l-1]和[1,r] 的异或结果相等
所以只需要预处理所有的前缀异或结果并统计结果的个数,如果区间[1,r]等0,那么就需要预处理一个0.
注意别爆int!!!血得教训啊,牛客还不显示过了几个样例,又越改错越多,太菜了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=500010,mod=998244353;
int n,k;
ll s[N],sum[N];
map<ll,ll> mp,mp1;
int main()
{
    cin>>n;
    ll ans=0,num0=0;
    mp[0]=1;
    for(int i=1;i<=n;i++)
    {
        cin>>s[i];
        sum[i]=sum[i-1]^s[i];
        mp[sum[i]]++;
    }
    for(auto it:mp)
    {
        ll y=it.second;
        ans+=((y-1)*y/2);
    }
    cout<<ans;
    return 0;
}```
E 假设有n个加号,就需要有n+1个数字,怎么使表达式最小呢,使各个数的数位尽量平均,还有高位数越小越好,预先统计各个数的数量往这几个数里面添加就行了,最后高精度计算就行了(复制粘贴真好..)

```cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=500010,mod=998244353;
int n,k;
string s;
int num[10],cnt;
vector<int> ve[N],ans;
vector<int> add(vector<int> &A, vector<int> &B)
{
	for(int i=0;i<(B.size()+1)/2;i++)
		swap(B[i],B[B.size()-i-1]);
    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()
{
	cin>>s;
	for(int i=0;i<s.size();i++)
		if(s[i]=='+') cnt++;
		else num[s[i]-'0']++;
	int sum=s.size()-cnt;
	int i=0,j=1,k=0;
	while(sum--)
	{
		while(!num[j]) j++;
		if(j>=10) break;
		ve[i].push_back(j);
		num[j]--;
		i=(i+1)%(cnt+1);
	}
	for(int i=ve[0].size()-1;i>=0;i--)
		ans.push_back(ve[0][i]);
	for(int i=1;i<=cnt;i++)
		ans=add(ans,ve[i]);
	for(int i=ans.size()-1;i>=0;i--)
		cout<<ans[i];
	return 0;
}

F 首先这是一颗树,只会有一个连通块…想太多了
首先,注意到输掉的唯一方法是自己的唯一一条边有另一个人,那么自己一定是在叶子上。
令两个人之间的距离为D。请注意,每人行动后D增加1或减少1。 因此,每有人走一步D的奇偶性都会改变。
假设最初,在牛牛移动之前,D是偶数。 那么当牛牛移动时D将始终为偶数,而当牛妹移动时D将始终为奇数。
请注意,只要牛牛和牛妹的令牌位于相邻的节点中,D=1。因此,如果D为偶数,则他们不在相邻的单元格中,并且牛牛始终可以移动。
另一方面,由于牛牛总是可以移动,因此他可以向牛妹的方向移动,将牛妹必然会最终移动到叶子上。同样,如果最初D是奇数,则牛妹获胜。
因此,答案取决于距离的奇偶性:如果是偶数,则牛牛获胜,否则牛妹获胜。
可以发现,只有牛牛的初始位置和牛妹的初始位置距离为偶数时,牛牛获胜。只需要分别求出深度为奇数的点和深度为偶数的点的数量即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=1000010,mod=998244353;
int n,k;
int s[N],dep[N];
ll cnt0,cnt1;
map<int,int> mp,mp1;
int main()
{
	cin>>n;
	dep[1]=1;
	cnt1++;
	for(int i=2;i<=n;i++)
	{
		cin>>s[i];
		dep[i]=dep[s[i]]+1;
		if(dep[i]&1)cnt1++;
		else cnt0++;
	}
	cout<<(cnt0-1)*cnt0+(cnt1-1)*cnt1<<endl;
	return 0;
}
posted @ 2020-02-20 14:34  Neflidata  阅读(3)  评论(0编辑  收藏  举报