zoukankan      html  css  js  c++  java
  • Winter Petrozavodsk Camp(7题)

    Winter Petrozavodsk Camp(7题)

    2020-2021 Winter Petrozavodsk Camp, Belarusian SU Contest (XXI Open Cup, Grand Prix of Belarus)

    C. Brave Seekers of Unicorns(dp+二进制推导)

    [dp[i]$$表示以i结束的方案数数量 $$dp[i]=sum_{1}^{i-1}(dp[j]-dp[ioplus j])]

    记$$k=ioplus j$$,则$$k<j<i$$

    变形后,dp[i]=sum_{1}^{i-1}dp[j]-sum_{k<koplus i<i}dp[k]

    我们来讨论后半段,以i=1011001,-表示无所谓
    i: 1 0 1 1 0 0 1

    j: 1 0 0 - - - -

    k: 0 0 1 - - - -

    注意:

    1.j第一位一定是1,不然j<k

    2.因为后面k取什么都可以,所以:对于i每一位为1的数,以当前为例就是第5位是1,前面是0的数都是合法的k,这就是dp[x*2-1]-dp[x-1]的由来

    #include<bits/stdc++.h>
     
    using namespace std;
    #define debug( x) cout<<#x<<':'<<x<<endl;
    typedef long long ll;
     
    const int mod=998244353;
    const int maxn=1e6+100;
    ll dp[maxn],sumdp[maxn];
     
    int main(){
        int n;
        scanf("%d",&n);
        sumdp[0]=0;
        for(int i=1;i<=n;i++){
            dp[i]=sumdp[i-1]+1;//+1表示单独i
            ll x=1;
            while(x*2<i){//首位1不可以
                if(x&i){
                    dp[i]=(dp[i]-sumdp[x*2-1]+sumdp[x-1]+mod)%mod;
                }
                x*=2;
            }
            sumdp[i]=(sumdp[i-1]+dp[i])%mod;
        }
        printf("%lld
    ",sumdp[n]);
    }
    

    D - Bank Security Unification(二进制dp)

    /*
     位运算dp:
     有一个dp性质是:
     到当前位置,有若干个位数为j的数组成的子序列:a1,a2,a3,...,ax;
     只需要用ax来更新当前位置就可以了,因为前面的dp[ai]<dp[ax]是显然的
     
     太奇怪了(严肃谴责)
    
    */
    
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    void Ios(){
     ios::sync_with_stdio(false);
        cin.tie(nullptr);cout.tie(nullptr);
    }
    const int maxn=5e6;
    const int qwq=60;
    ll a[maxn],dp[qwq],sum[maxn],pos[qwq];
    int main(){
       Ios();
        ll n;cin>>n;
        for(ll i=1;i<=n;i++){
            cin>>a[i];
        } 
        ll ans=0;a[0]=0;memset(pos,0,sizeof(pos));memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++){
            ll top=0,imax=0,temp=a[i];
            for(int j=0;j<qwq;j++){ 
                imax = max(imax, dp[j] + (a[i] & a[pos[j]]));
            }
            while(temp){
                ++top;temp>>=1;
            }
            
            dp[top]=imax;
            pos[top]=i;
            ans=max(ans,imax);
        }
        cout<<ans<<endl;
    }
    

    G - Biological Software Utilities(找规律+矩阵树定理)

    简述一下题意:

    这道题就是1-n顶点的树,我们要找到有多少棵树,删除一些边后是由两两连通块组成的。

    非常正确的想法是:

    把两两分堆然后拼起来。

    1.首先要知道label tree有n^(n-2)个

    ​ 具体见https://www.cnblogs.com/zx0710/p/14475040.html

    ​ 建议还是当结论记下来,毕竟赛场上推矩阵树定理未免有点难顶

    2.![img](file:///C:Userszx200DocumentsTencent Files2505986089ImageC2CIRXK5XAYC@H_}CO%W0APB.png)

    每个连通块间有4中连接方式

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define debug(x) cout<<x<<':'<<x<<endl;
    const int maxn=3e6+100;
    const int mod=998244353;
    ll fac[maxn],inv[maxn];
    ll  qpow(ll a,ll n){
        ll ans=1;
        while(n){
            if(n&1){
                ans=ans*a%mod;
            }
            a=a*a%mod;
            n>>=1;
        }
        return ans;
    }
    int main(){
    
        ll n,ans;scanf("%lld",&n);
        if(n%2==1)ans=0;
        else if(n==2)ans=1;
        else{
            ans=qpow(n/2,n/2-2)*qpow(2,n/2-2)%mod;
            for(int i=n/2+1;i<=n;i++)ans=ans*i%mod;
        }
        printf("%lld
    ",ans);
    }
    
    

    I - Binary Supersonic Utahraptors(签到题)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=1e6+50;
    const int mod=998244353;
    int a[100];
    int main(){
        ll n,m,k;
        scanf("%lld%lld%lld",&n,&m,&k);
        ll temp;
        for(int i=1;i<=n+m;i++){
            scanf("%lld",&temp);a[temp]++;
        }
        printf("%lld",abs(a[0]-m));
        //system("pause");
    }
    

    J - Burnished Security Updates(判二分图)

    /*
    读懂题意胜过一切。
    读懂之后发现:要判一下是不是二分图。
    是二分图的话,输出一下两边最少点的数量。
    因为可能是森林,所以每dfs一次判一下。
    */
     
    #include<bits/stdc++.h>
     
    using namespace std;
    #define debug( x) cout<<#x<<':'<<x<<endl;
    typedef long long ll;
     
    const int maxn=1e6+100;
     
    struct node{
        int to,nxt;
    }edge[maxn<<2];
     
    int tot=0,head[maxn];
     
    void addedge(int u,int v){
        ++tot;
        edge[tot].to=v;
        edge[tot].nxt=head[u];
        head[u]=tot;
    }
     
    bool flag=true;
    int n,m,u,v,color[maxn],sum1,sum2;
     
    void dfs(int root ,int c){
        color[root]=c;
        if(c==1) sum1++;
        else sum2++;
        for(int i=head[root];i!=-1;i=edge[i].nxt){
            int node=edge[i].to;
            if(color[node]==-1)dfs(node,3-c);
            else if(color[node]!=3-c) flag=false;
        }
    }
     
    int main(){
        scanf("%d%d",&n,&m);
        memset(head,-1,sizeof(head));
        while(m--){
            scanf("%d%d",&u,&v);
            addedge(u,v);addedge(v,u);
        }
        memset(color,-1,sizeof(color));
        int ans=0;
        for(int i=1;i<=n;i++){
            if(color[i]==-1){
                sum1=0,sum2=0;
                dfs(i,1);
                ans+=min(sum1,sum2);
            }
        }
        if(flag==false) printf("-1");
        else printf("%d",ans);
        //system("pause");
    }
    

    M - Brilliant Sequence of Umbrellas

    /*
    一开始一直在想要怎么构造,然后枚举了一下,过了
    */
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll N =2e7+10;//
    bool notp[N];
    int prime[N], pnum;
    void sieve()
    {
        //[0,N]的素数表
        memset(notp, 0, sizeof(notp));//0为素数;
        notp[0] = notp[1] = 1;
        pnum = 0;
        for (int i = 2; i <= N; i++)
        {
            if (!notp[i])  prime[++pnum] = i;
            for (int j = 1; j <= pnum && prime[j] * i <= N; j++)
            {
                notp[prime[j] * i] = 1;
                if (i % prime[j] == 0) break;
            }
        }
    }
    ll a[1000005];
    int main(){
        ll n;
        sieve();
        
        scanf("%lld",&n);
        ll k=ceil(2.0/3.0*sqrt(n));
        printf("%lld
    ",k);
        a[1]=1;a[2]=2;
       
        for(ll i=3;i<=k;i++) a[i]=1ll*prime[i-2]*prime[i-1];
        a[k]=prime[k-2];
        for(ll i=1;i<=k;i++){
            printf("%lld ",a[i]);
        }
    }
    
    

    N - Best Solution Unknown(记忆化迭代)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=2e6+10;
    #define debug(x) cout<<#x<<':'<<x<<endl;
    int a[maxn];int l[maxn],r[maxn];
    void judge(int x){
        bool flag=false;
        int lx=l[x]-1;
        if(a[x]+r[x]-l[x]>=a[lx]){//打败左边缘的数
            flag=true;
            l[x]=min(lx,l[lx]);
            r[x]=max(r[x],r[lx]);
        }
        int rx=r[x]+1;
        if(a[x]+r[x]-l[x]>=a[rx]){//打败右边缘的数
            flag=true;
            r[x]=max(rx,r[rx]);
            l[x]=min(l[x],l[rx]);
        }
        if(flag) judge(x);
    }
    int main(){
        int n;scanf("%d",&n);
        vector<pair<int,int>>vc;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            vc.push_back({a[i],i});
            l[i]=i;r[i]=i;
        }
        a[0]=a[n+1]=0x3f3f3f3f;//一定要足够大
        sort(vc.begin(),vc.end());
        vector<int>ans;
        for(int i=0;i<n;i++){
            judge(vc[i].second);
        }
        for(int i=1;i<=n;i++){
            if(l[i]==1&&r[i]==n){
                ans.push_back(i);
            }
        }
        printf("%d
    ",ans.size());
        for(int i=0;i<ans.size();i++){
            cout<<ans[i]<<' ';
        }
    }
    
  • 相关阅读:
    C# 普印力RFID打印机模板打印
    C# IP地址段端口扫描器封装调用
    W3100SMS 短信猫代码发送 下
    C#动态创建Access数据库,创建加密码Access数据库,更改Access密码
    C# 区分键盘和红外线扫描枪输入 模拟扫描枪
    文件格式大全
    winform获取当前Url地址
    [WPF]使用C#代码实现DataTemplate
    钩子类型
    WM_*
  • 原文地址:https://www.cnblogs.com/zx0710/p/14475991.html
Copyright © 2011-2022 走看看