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[i\oplus j])\]

記$$k=i\oplus j$$,則$$k<j<i$$

變形后,dp[i]=\sum_{1}^{i-1}dp[j]-\sum_{k<k\oplus 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\n",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:\Users\zx200\Documents\Tencent Files\2505986089\Image\C2C\IRXK5XAYC@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\n",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\n",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\n",ans.size());
    for(int i=0;i<ans.size();i++){
        cout<<ans[i]<<' ';
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM