1. 程式人生 > >SDOI2016 Round 1解題報告

SDOI2016 Round 1解題報告

Day1
T1
題目大意:
已知n,m,k,求n1i=0m1j=0max((ixorj)k,0)

題解:
可以按照數位dp的思想來做,每次考慮在當前這個數的二進位制位和n,m這兩個二進位制位的大小,以及與k的大小,從狀態f[i][j][k][l]轉移到後繼狀態,複雜度是O(log(n)2223)
但是數位dp畢竟難寫難調,我們可以繼續考慮,有一棵log(n)層的滿二叉Trie,那它的每個葉子節點都可以對應一個0n1的數。那我們就可以根據這個想法一部分一部分的來考慮他們對答案的貢獻了(左子樹>右子樹),比較好寫。
Ps.對於取模中的除法運算,如x=(a/2)m

odP可以轉化成x=((amod(2P))/2)modP

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 70
#define LL long long
LL n,m,K,P,ans,mi[N];
inline int in(){
    int x=0; char ch=getchar(); bool
f=1; while (ch<'0'||ch>'9'){ if (ch=='-') f=0; ch=getchar(); }while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); if (!f) x=-x; return x; } inline LL Lin(){ LL x=0; char ch=getchar(); bool f=1; while (ch<'0'||ch>'9'){ if (ch=='-'
) f=0; ch=getchar(); }while (ch>='0'&&ch<='9') x=x*10ll+(LL)(ch-'0'),ch=getchar(); if (!f) x=-x; return x; } inline LL calc(LL x,LL y){ LL s=(((x+y)%(P*2ll))*((y-x+1)%(P*2ll)))%(P*2ll); s/=2ll,s%=P; return s; } inline LL work(LL x,LL y,int xx,int yy){ LL s=0,z; if (xx<yy) swap(x,y),swap(xx,yy); x^=(x&(mi[xx]-1)),y^=(y&(mi[xx]-1)),z=x^y; if (z>K) s=calc(z-K,z+mi[xx]-K-1); else if ((z+mi[xx])>K) s=calc(0,z+mi[xx]-K-1); s=(s*(mi[yy]%P))%P; return s; } int main(){ int T=in(),i,j,tt; LL x,y; mi[0]=1ll; for (i=1; i<=62; i++) mi[i]=mi[i-1]<<1; for (tt=1; tt<=T; tt++){ n=Lin(),m=Lin(),K=Lin(); P=Lin(),x=0ll,ans=0ll; for (i=62; i>=0; i--){ if (n&mi[i]){ y=0ll; for (j=62; j>=0; j--){ if (m&mi[j]){ ans=(ans+work(x,y,i,j))%P,y|=mi[j]; } }x|=mi[i]; } }printf("%I64d\n",ans); }return 0; }

T2
題目大意:
給出n個數,對於兩個數x,y,它們能配對的條件是相差一個質因子,配對成功可以獲得cx×cy,每個數有bi個可用,求在配對的價值和不小於0的情況下最多能匹配多少對。

題解:
最大費用最大流。題目中給出的是一個二分圖,對其建網路流圖:
S>每個奇數個質因子的數,流量為bi,費用為0
每個奇數個質因子的數>每個能配對的偶數個質因子的數,流量為inf,費用為ci×cj
每個偶數個質因子的數>T,流量為bi,費用為0
然後跑最大費用流,直到某次增廣後總費用為負,計算一下最後跑的流量即可。

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 610
#define M 600010
#define P 2000000
#define inf 1000000000
#define limit -100000000000000ll
#define LL long long
struct A{
    int x,y,k; LL z;
}a[N];
struct e{
    int u,v,cap,next; LL cost;
}e[M*2];
int n,ans=0,num=1,S,T,head[N],pre[N],flow[N],q[M*4];
bool vis[N]; LL dis[N],sum=0;
inline int in(){
    int x=0; char ch=getchar(); bool f=1;
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=0;
        ch=getchar();
    }while (ch>='0'&&ch<='9')
        x=x*10+ch-'0',ch=getchar();
    if (!f) x=-x; return x;
}
inline LL Lin(){
    LL x=0; char ch=getchar(); bool f=1;
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=0;
        ch=getchar();
    }while (ch>='0'&&ch<='9')
        x=x*10ll+(LL)(ch-'0'),ch=getchar();
    if (!f) x=-x; return x;
}
inline void add(int u,int v,int cap,LL cost){
    e[++num].u=u,e[num].v=v,e[num].cap=cap;
    e[num].cost=cost,e[num].next=head[u],head[u]=num;
    e[++num].u=v,e[num].v=u,e[num].cap=0;
    e[num].cost=-cost,e[num].next=head[v],head[v]=num;
}
inline void divide(int xu){
    int i,x=a[xu].x,y;
    a[xu].k=0,y=sqrt(x);
    for (i=2; i<=sqrt(x); i++){
        while (!(x%i)) x/=i,a[xu].k++;
    }if (x>1) a[xu].k++;
}
inline bool spfa(){
    int i,u,v,h=0,t=1;
    for (i=S; i<=T; i++)
        vis[i]=0,dis[i]=limit;
    LL minn=dis[0];
    q[h]=S,dis[S]=0ll,flow[S]=inf,vis[S]=1;
    while (h<t){
        u=q[h%P],h++,vis[u]=0;
        for (i=head[u]; i; i=e[i].next){
            v=e[i].v;
            if (e[i].cap>0&&dis[v]<dis[u]+e[i].cost){
                dis[v]=dis[u]+e[i].cost,pre[v]=i;
                flow[v]=min(flow[u],e[i].cap);
                if (!vis[v]) q[t%P]=v,t++,vis[v]=1;
            }
        }
    }if (dis[T]>minn) return true;
    else return false;
}
int main(){
    int i,j; n=in(),S=0,T=n+1;
    for (i=1; i<=n; i++) a[i].x=in();
    for (i=1; i<=n; i++) a[i].y=in();
    for (i=1; i<=n; i++) a[i].z=Lin();
    for (i=1; i<=n; i++){
        divide(i);
        if (a[i].k&1) add(S,i,a[i].y,0);
        else add(i,T,a[i].y,0);
    }for (i=1; i<=n; i++){
        if (a[i].k&1){
            for (j=1; j<=n; j++){
                if (i==j) continue;
                if (!(a[j].k&1)){
                    if (!(a[i].x%a[j].x)&&a[i].k==a[j].k+1)
                        add(i,j,inf,a[i].z*a[j].z);
                    if (!(a[j].x%a[i].x)&&a[j].k==a[i].k+1)
                        add(i,j,inf,a[i].z*a[j].z);
                }
            }
        }
    }while (spfa()){
        if ((sum+dis[T]*flow[T])<0){
            ans+=(int)(sum/(-dis[T])); break;
        }else {
            for (i=T; i!=S; i=e[pre[i]].u){
                e[pre[i]].cap-=flow[T];
                e[pre[i]^1].cap+=flow[T];
            }ans+=flow[T],sum+=dis[T]*flow[T];
        }
    }printf("%d\n",ans);
    return 0;
}

T3
題目大意:
給出一棵n個節點的樹,每次操作選一條鏈,用一個自變數是和出發點的距離的一次函式更新最小值,多次詢問某條鏈的最小值。

題解:
據說這個題是李超樹,國家集訓隊作業題掛到了樹上(聽說qdez的神犇們都做過。。。)
用線段樹維護覆蓋當前區間的線段,更新的時候分情況討論,如果可以更新就下傳當前線段,然後標記新線段,每次線段最多被分成log(n)個區間,下放最多log(n)層,算上樹鏈剖分,總的複雜度是O(nlog3(n))

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100010
#define inf 123456789123456789ll
#define LL long long
#define root 1,1,n
#define lch rt<<1,l,mid
#define rch rt<<1|1,mid+1,r
struct E{
    int v,next; LL k;
}e[N<<1];
struct Line{
    LL k,b;
    inline LL F(LL x){
        return k*x+b;
    }
};
struct Tr{
    Line x; LL minn; bool f;
}tr[N<<2];
int n,m,num=0,tot=0,head[N],cur[N],fa[N][20],de[N],w[N],top[N],sz[N],son[N],q[N]; LL ans,dis[N],Dis[N];
inline int in(){
    int x=0; char ch=getchar(); bool f=1;
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=0;
        ch=getchar();
    }while (ch>='0'&&ch<='9')
        x=x*10+ch-'0',ch=getchar();
    if (!f) x=-x; return x;
}
inline LL Lin(){
    LL x=0ll; char ch=getchar(); bool f=1;
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=0;
        ch=getchar();
    }while (ch>='0'&&ch<='9')
        x=x*10ll+(LL)(ch-'0'),ch=getchar();
    if (!f) x=-x; return x;
}
inline void add(int u,int v,LL k){
    e[++num].v=v,e[num].k=k;
    e[num].next=head[u],head[u]=num;
}
inline void BFS(){
    int i,j,u,v,h=0,t=1; q[h]=1,dis[1]=0;
    for (i=1; i<=n; i++) cur[i]=head[i];
    while (h<t){
        u=q[h++];
        for (i=1; (1<<i)<=de[u]; i++)
            fa[u][i]=fa[fa[u][i-1]][i-1];
        for (i=head[u]; i; i=e[i].next){
            v=e[i].v; if (v==fa[u][0]) continue;
            fa[v][0]=u,de[v]=de[u]+1;
            dis[v]=dis[u]+e[i].k,q[t++]=v;
        }
    }for (i=t-1; i>=0; i--){
        u=q[i],sz[u]=1,son[u]=0;
        for (j=head[u]; j; j=e[j].next){
            v=e[j].v; if (v==fa[u][0]) continue;
            sz[u]+=sz[v]; if (sz[son[u]]<sz[v]) son[u]=v;
        }
    }for (i=0; i<t; i++){
        u=q[i];
        if (!w[u]){
            for (j=u; j; j=son[j])
                w[j]=++tot,Dis[tot]=dis[j],top[j]=u;
        }
    }
}
inline int lca(int x,int y){
    if (de[x]<de[y]) swap(x,y);
    int i,k=de[x]-de[y];
    for (i=18; i>=0; i--)
        if (k&(1<<i)) x=fa[x][i];
    if (x==y) return x;
    for (i=18; i>=0; i--)
        if (fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    if (x==y) return x;
    return fa[x][0];
}
inline void push_up(int rt){
    tr[rt].minn=min(tr[rt].minn,min(tr[rt<<1].minn,tr[rt<<1|1].minn));
}
inline void build(int rt,int l,int r){
    tr[rt].minn=inf,tr[rt].f=0;
    if (l==r) return;
    int mid=(l+r)>>1;
    build(lch),build(rch);
    push_up(rt);
}
inline void change(int rt,int l,int r,int ll,int rr,Line x){
    if (ll<=l&&r<=rr){
        tr[rt].minn=min(tr[rt].minn,min(x.F(Dis[l]),x.F(Dis[r])));
        if (!tr[rt].f){
            tr[rt].f=1,tr[rt].x=x;
            return;
        }if (l==r){
            if (x.F(Dis[l])<tr[rt].x.F(Dis[l])) tr[rt].<