zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    Linux 开发之线程条件锁那些事
    Linux 开发之线程条件锁那些事
    Linux 开发之线程条件锁那些事
    洛谷3919:可持久化数组——题解
    洛谷3919:可持久化数组——题解
    洛谷3919:可持久化数组——题解
    洛谷3919:可持久化数组——题解
    长篇干货|以太坊智能合约 —— 最佳安全开发指南(附代码)
    长篇干货|以太坊智能合约 —— 最佳安全开发指南(附代码)
    长篇干货|以太坊智能合约 —— 最佳安全开发指南(附代码)
  • 原文地址:https://www.cnblogs.com/neflibata/p/12871786.html
Copyright © 2011-2022 走看看