2017-11-5離線賽總結

題目

3808,3809,3810

失分小結

估分

100+40+10
(第三題切分切太多dfs沒寫…)

實際分數

100+20+30
第二題炸了其實最好能寫到80

題解

T1

P100

我是直接敲P100代碼實話說我還真不知道P70怎麼寫…
//加讀入掛16ms,不加30ms

CODE
#include<cstdio>
#define N 200005
typedef long long LL;
const LL P=1e9+7;
int A[N];//數組還可以優化
inline void rd(int &x) {
    x=0;char c;
    while(c=getchar(),c<48);
    do x=(x<<3)+(x<<1)+(c&15);
    while(c=getchar(),c>47);
}
int main() {
    int n;
    rd(n);
    for(int i=0; i<=n; i++)rd(A[i]);
    LL sum=(A[0]+A[1])%P;
    LL res=(2LL*A[0]*A[1])%P;
    LL POW=1;
    for(int i=2; i<=n; i++) {
        POW=(POW<<1)%P;
        res=(((res+sum*A[i])%P)<<1)%P;
        sum=(sum+A[i]*POW)%P;
    }
    printf("%lld\n",res);
    return 0;
}

T2

平面圖上的MST.
普通MST可以寫到P40,但是由於平面圖的特殊性,可以進行各種優化.

P70-80

CODE
#include<cstdio>
#include<cmath>
#include<algorithm>
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;i++)
#define M 1000005
using namespace std;
typedef long long LL;
int Y1[M],Y2[M],fa[M*2];
LL Sum1[M],Sum2[M];
struct node {
    int x,y;
    double len;
} E[5*M];
template<class T>void rd(T &x) {
    x=0;static char p;
    while(p=getchar(),p<48);
    do x=(x<<1)+(x<<3)+(p&15);
    while(p=getchar(),p>47);
}
bool cmp(node w1,node w2) {return w1.len<w2.len;}
double ans=0;
LL sqr(LL x) {return x*x;}
int getfa(int x) {return fa[x]==x?x:fa[x]=getfa(fa[x]);}
int main() {
    int n,m,x1,x2,tot=0;
    rd(n),rd(m),rd(x1),rd(x2);
    FOR(i,1,n)rd(Y1[i]),Sum1[i]=Sum1[i-1]+Y1[i];
    FOR(i,1,m)rd(Y2[i]),Sum2[i]=Sum2[i-1]+Y2[i];
    int tmp=sqr(x1-x2);
    FOR(i,1,n-1)E[++tot]=(node) {i,i+1,Y1[i+1]};
    FOR(i,1,m-1)E[++tot]=(node) {n+i,n+i+1,Y2[i+1]};
    FOR(i,1,n) {
        int id=lower_bound(Sum2+1,Sum2+1+m,Sum1[i])-Sum2;
        if(id==m+1)id--;
        if(Sum2[id]==Sum1[i])E[++tot]=(node) {i,n+id,x2-x1};
        else if(Sum2[id]<Sum1[i])E[++tot]=(node) {
            i,n+id,sqrt(tmp+sqr(Sum1[i]-Sum2[id]))};
        else {
            E[++tot]=(node) {i,n+id,sqrt(tmp+sqr(Sum1[i]-Sum2[id]))};
            id--;
            if(!id)continue;
            E[++tot]=(node) {i,n+id,sqrt(tmp+sqr(Sum1[i]-Sum2[id]))};
        }
    }
    sort(E+1,E+1+tot,cmp);
    FOR(i,1,n+m)fa[i]=i;
    FOR(i,1,tot) {
        int x=getfa(E[i].x),y=getfa(E[i].y);
        if(x==y)continue;
        ans+=E[i].len;
        fa[x]=y;
    }
    printf("%.2f\n",ans);
    return 0;
}

P100

顯然最簡(bian)單(tai)的還是dp . 時間複雜度O(n+m) .
定義f[i][j][0 or 1] 表示左邊第i 個點與右邊第j 個點一下的點已經處理完畢,左i 點與右j 點連不連通.那麼f[i][j] 可以從f[i1][j]f[i][j1] 轉移.
轉移方程見CODE.
維護i,j 相鄰,這樣最多會用到n+m 個狀態.
顯然i,j 都可以滾動.

CODE
#include<cstdio>
#include<cmath>
#include<algorithm>
#define M 1000005
using namespace std;
template<typename T>void rd(T &x) {
    x=0;static char c;
    while(c=getchar(),c<48);
    do x=(x<<3)+(x<<1)+(c&15);
    while(c=getchar(),c>47);
}
int Y1[M],Y2[M];
double f[M][2][2];
int n,m;
long long x1,x2,x;
double t;
inline long long sqr(register int x){return 1LL*x*x;}
inline double dist(int i,int j) {return sqrt(x+sqr(Y1[i]-Y2[j]));}
int main() {
    rd(n),rd(m),rd(x1),rd(x2);
    x=sqr(x1-x2);
    for(int i=1; i<=n; i++) {
        rd(Y1[i]);
        if(i>1)Y1[i]=Y1[i]+Y1[i-1];
    }
    for(int i=1; i<=m; i++) {
        rd(Y2[i]);
        if(i>1)Y2[i]=Y2[i]+Y2[i-1];
    }
    f[1][1][1]=dist(1,1);
    int j=1,cur=1;
    for(int i=1; i<=n; i++) {
        if(i>1) {
            t=dist(i,j);
            f[i][cur][1]=min(f[i-1][cur][1]+Y1[i]-Y1[i-1],min(f[i-1][cur][1]+t,f[i-1][cur][0]+Y1[i]-Y1[i-1]+t));
            f[i][cur][0]=min(f[i-1][cur][1],f[i-1][cur][0]+Y1[i]-Y1[i-1]);
        }
        while(j<m&&(Y1[i]>=Y2[j]||i==n)) {
            t=dist(i,++j);
            cur=!cur;
            f[i][cur][1]=min(f[i ][!cur][1]+Y2[j]-Y2[j-1],min(f[i][!cur][1]+t,f[i][!cur][0]+Y2[j]-Y2[j-1]+t));
            f[i][cur][0]=min(f[i ][!cur][1],f[i][!cur][0]+Y2[j]-Y2[j-1]);
        }
    }
    printf("%.2lf\n",f[n][m&1][1]);
    return 0;
}

T3

P10

n<=7O(nn) 枚舉每個人的編號模擬.(dfs)

P30

n<=10O(n!) 枚舉排列.

P50

n<=18 :狀壓dp,dp[i][j] 表示第i 個人固定了位置後每個位置佔用狀態爲j .

CODE
#include<cstdio>
#include<memory.h>
#include<cmath>
#define S 20
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
using namespace std;
template<typename T>inline void Rd(T &x) {
    x=0;
    register char c;
    while(c=getchar(),c<48);
    do x=(x<<3)+(x<<1)+(c&15);
    while(c=getchar(),c>47);
}
int n,m,P;
int B[35];
bool mark[S];
int dp[S][1<<S];

int main() {
    scanf("%d%d%d",&n,&m,&P);
    FOR(i,1,m) {
        int x,pos;
        scanf("%d%d",&x,&pos);
        mark[x]=1;
        B[x]=pos;
        B[x]--;
    }
    int tot=(1<<n)-1;
    memset(dp,-1,sizeof dp);
    dp[0][0]=1;
    FOR(k,1,n)FOR(i,0,tot)if(~dp[k-1][i])
        FOR(j,B[k],n-1)if(!(i&(1<<j))) {
            if(!~dp[k][i|(1<<j)])
                dp[k][i|(1<<j)]=0;
            dp[k][i|(1<<j)]+=dp[k-1][i];
            dp[k][i|(1<<j)]%=P;
            if(mark[k])break;
        }
    printf("%d\n",dp[n][tot]==-1?0:dp[n][tot]);
    return 0;
}

P60

m==0m==1 時特判。這樣多P10.
這裏加上了P50的代碼

CODE
#include<cstdio>
#include<cstdlib>
#include<memory.h>
#include<algorithm>
#define FOR(i,a,b) for(int i=(a),i##_END_=(b);i<=i##_END_;i++)
#define N 35
#define S 20
using namespace std;
int peo[N];
struct node {
    int p,q;
    bool operator<(const node _)const {return p<_.p;}
} A[N];

template<typename T>inline void Rd(T &x) {
    x=0;register char c;
    while(c=getchar(),c<48);
    do x=(x<<3)+(x<<1)+(c&15);
    while(c=getchar(),c>47);
}
int B[N];
bool mark[S];
int dp[S][1<<S];

int main() {
    int n,m,M;
    scanf("%d %d %d",&n,&m,&M);
    if(m==0) {
        long long ans=1;
        for(int i=2; i<=n; i++)
            ans=(ans*i)%M;
        printf("%lld\n",ans);
        exit(0);
    }
    if(m==1) {
        long long f[N],A[N],ans=0;
        int p,q;
        scanf("%d %d",&p,&q);
        A[0]=f[0]=f[1]=1,A[1]=p-1;
        for(int i=2; i<=n; i++) {
            f[i]=(f[i-1]*i)%M;
            A[i]=(A[i-1]*(p-i))%M;
        }
        for(int i=0; i<=min(p-1,n-q); i++) {
            ans=(ans+A[i]*f[n-1-i])%M;
        }
        printf("%lld\n",ans);
        exit(0);
    }

    FOR(i,1,m) {
        int x,pos;
        scanf("%d%d",&x,&pos);
        mark[x]=1;
        B[x]=pos;
        B[x]--;
    }
    int tot=(1<<n)-1;
    memset(dp,-1,sizeof dp);
    dp[0][0]=1;
    FOR(k,1,n)FOR(i,0,tot)if(~dp[k-1][i])
        FOR(j,B[k],n-1)if(!(i&(1<<j))) {
            if(!~dp[k][i|(1<<j)])
                dp[k][i|(1<<j)]=0;
            dp[k][i|(1<<j)]+=dp[k-1][i];
            dp[k][i|(1<<j)]%=M;
            if(mark[k])break;
        }
    printf("%d\n",dp[n][tot]==-1?0:dp[n][tot]);
    return 0;
}

P100

首先有一個比較顯然的性質:若第i 個人本來要到j 位置 現在只能到k 位置 那麼在jk 之間的人一定編號比i 小,可以dfs 每個固定位置的人最後在什麼位置,那麼每次需要知道當前已經有人的位置和已經選好位置的人數。在比當前人編號小的人裏面可以選出一些在當前人的前面,這些人的位置又不固定,所以要用排列組合算出方案數。全部確定完之後可能還有人沒有固定要再排列一下。
而由於可以通過可行性剪枝剪掉很多不合法的情況,dfs 還是很快滴。位置的狀態可以用二進制壓位表示,極限時間複雜度爲O(n×nm) .
另外,由於m 是指數,因此當m 過大而n 不大時可以採用P50狀壓dp.

CODE
#include<cstdio>
#include<memory.h>
#include<algorithm>
#define N 35
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
int n,m,M;
struct node {int p,q;} D[10];

int C[N][N],fac[N],A[N][N];
bool mark[N];
int ans;
bool cmp(node _,node __){return _.p<__.p;}
int x=1;
void dfs(int res,int tmp) {
    res+=D[x].p-D[x-1].p-1;
    if(x>m) {
        tmp=1ll*tmp*fac[res]%M;
        ans=(ans+tmp)%M;
        return;
    }
    int sum=0;
    bool used[N];
    memcpy(used,mark,sizeof used);
    for(int i=D[x].q; i<=n; i++)if(!mark[i]) {
            if(sum>res)break;
            mark[i]=1;
            x++,dfs(res-sum,1ll*tmp*A[res][sum]%M),x--;
            sum++;
        }
    memcpy(mark,used,sizeof mark);
}
int main() {
    scanf("%d%d%d",&n,&m,&M);
    FOR(i,1,m)scanf("%d%d",&D[i].p,&D[i].q);
    C[0][0]=C[1][0]=C[1][1]=fac[0]=fac[1]=1;
    FOR(i,2,30) {
        C[i][0]=C[i][i]=1;
        fac[i]=1ll*fac[i-1]*i%M;
        FOR(j,1,i-1)C[i][j]=(C[i-1][j]+C[i-1][j-1])%M;
    }
    FOR(i,0,30)FOR(j,0,i)A[i][j]=1LL*C[i][j]*fac[j]%M;
    std::sort(D+1,D+1+m,cmp);
    D[m+1].p=n+1;
    D[0].p=0;
    dfs(0,1);
    printf("%d\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章