1. 程式人生 > >10.2考試總結

10.2考試總結

freopen 新的 情況 ati 二進制 num turn 考試 edge

10.2考試試題

A、同余方程:

Description:

\(給定求的解數\text{給定}l_1,r_1,l_2,r_2,m \text{求}x\in[l_1,r_1],y\in[l_2,r_2],x\text{^}y\equiv 0\pmod{m}的解數\)

\(1<=l_1,l_2,r_1,r_2<=10^{18},1<=m<=10^9\)

Solution:

我們先將區間化為前綴,\([l_1,r_1]\times[l_2,r_2]\)化成\(([0,r_1]-[0,l_1-1])\times([0,r_2]-[0,l_2-1])=[0,r_1]\times[0,r_2]-[0,l_1]\times[0,r_2]-[0,l_2]\times[0,r_1]+[0,l_1]\times[0,l_2]\)

剩下的,我們來考慮前綴怎麽辦:\([0,a)\times[0,b)的貢獻\)
先從簡單的來,對於\([0,2^n)\times[0,2^n)\)

\(A\in[0,2^n), B\in[0,2^n)\)

\(A\)任選時,我們存在唯一的\(B\),使\(A\text{^}B=km\)

\(而的值域而A\text{^}B的值域I\subset [0,2^n)\) ,\(所以\)\(我只要計算出I中m的倍數\times 2^n\)

再難一點,對於\([0,2^n)\times (0,2^m)不妨設n<=m\)

\(當任意選時我們存在唯一的使註意反之不同因為當A任意選時,我們存在唯一的B,使A\text{^}B=km,註意反之不同(因為m>=n)\)

\(而的值域我們只要計算出中的的倍數而A\text{^}B的值域I\subset [0,2^n),我們只要計算出I中的m的倍數\times 2^n\)

最後我們在考慮\([0,a)\times[0,b)\)

我們將兩個數個數變成二進制,並逐位考慮

\(假如a的倒數第位為1,b的倒數第位為a,不妨設假如a的倒數第n位為1,b的倒數第m位為1,不妨設n<=m\)

\(那麽如果將這兩位的變成,那麽我們後位的後位都可以隨便設這個思維像是數位那麽如果將這兩位的1變成0,那麽我們a後n-1位,b的後m-1位,都可以隨便設,這個思維像是數位DP\)

那麽這個問提可以向上一個問題歸納,類比上問題,得到新的區間\([a-2^{n-1},a),[b-2^{m-1},b)\)

如果將上一行區間進行偏移的話,就可以得到類似上一個問題的區間,\([0,2^{n-1})\times[0,2^{m-1})\)

\([a-2^{n-1},a)\times[b-2^{m-1},b)\) 這樣的區間,它的左端點應該是將a的後m位清空(我們可以辦到因為m>=n)的值

右端點為左端點\(+2^{m-1}\),值域就是如此,再\(\times 2^{n-1}\)

綜上所述,我們解決了這個問題

code:

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const long long mod=998244353;
long long l1,l2,r1,r2,M;
inline long long cal(long long a,long long b){
    long long ans=0;
    for(long long i=0;i<=60;++i)
        for(long long j=0;j<=60;++j){
            if( ( a & ( 1ll << i ) ) && ( b & ( 1ll << j ) ) ){
                long long n=min(i,j);
                long long m=max(i,j);
                long long w=(1ll<<n)%mod;
                long long l=((a-(1ll<<i))^(b-(1ll<<j)))&(~((1ll<<m)-1ll));//與下同
                //long long l=((a-(1ll<<i)&(~((1ll<<m)-1ll)))^(b-(1ll<<j)))&(~((1ll<<m)-1ll));
                long long r=l+(1ll<<m)-1ll;
                long long num=(r/M-l/M+((l%M)==0))%mod;
                ans=(ans+num*w)%mod;
            }
        }
    return ans;
}
int main(){
    cin>>l1>>r1>>l2>>r2>>M; 
    cout<<(cal(r1+1,r2+1)-cal(l1,r2+1)-cal(l2,r1+1)+cal(l1,l2)+mod)%mod;
    return 0;
}

B、旅遊:

給定一張無向圖,第\(i\)條邊的邊權為\(2^i\),從1號點出發,要求每條邊至少走一遍,並回到1號,求最小代價

無向圖歐拉回路的判定:每個點的度數為偶數。

如果所有的點度數為偶數,那時我們只用把所有的邊加一遍即可,依靠這個思路,我們可以加上一些邊使所有點的度數為偶數,因為所有邊的邊權為2的冪,所以我們增加的邊一定是在MST上。

所以我們可以在最小生成樹上遞推:如果我們的葉子的度數為奇數,我們必須將葉子到他的父親的邊加1,根據這個,我們可以推到根節點。註意根節點沒有父親,所以他不能調整,但實際上因為任意一張無向圖所有點的度數之和為偶數,不可能出現只有一個根節點為奇數的情況

code:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cctype>
using namespace std;
const int mod=998244353;
const int MAXX=500010; 
int n,m,e[MAXX],deg[MAXX];
int head[MAXX],ver[MAXX<<1],nxt[MAXX<<1],edge[MAXX<<1];
int f[MAXX];
int tot,ans;
inline int find(int x){
    if(f[x]==x)return x;
    else return f[x]=find(f[x]);
}
inline int read(){
    int x=0;bool f=0;
    char c=getchar();
    while(!isdigit(c)){
        if(c==‘-‘)f=1;
        c=getchar();
    }
    while(isdigit(c)){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return f?-x:x;
}
inline void add(int x,int y,int z){
    ver[++tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
    edge[tot]=z;
}
inline void dfs(int x,int fa,int val){
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i];
        if(y==fa)continue;
        dfs(y,x,edge[i]);
    }
    if(deg[x]&1){
        deg[x]++;
        deg[fa]++;
        ans=(ans+val)%mod;
    }
}
int main(){
    freopen("B.in","r",stdin);
    freopen("B.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<=n;++i)f[i]=i;
    int temp=2;
    for(int i=1;i<=m;++i){
        e[i]=temp;
        temp=temp*2%mod;
        ans=(ans+e[i])%mod;
    }
    for(int i=1;i<=m;++i){
        int x,y;
        x=read();y=read();
        deg[x]++;deg[y]++;
        int fu=find(x);
        int fv=find(y);
        if(fu==fv)continue;
        f[fu]=fv;
        add(x,y,e[i]);
        add(y,x,e[i]);
    }
    dfs(1,0,0);
    printf("%d\n",ans);
    return 0;
}

10.2考試總結