2017-10-15离线赛

大体状况

195/300
爆炸= =

T1 draw

分析

Q1直接交换。
Q2半天想不出来。
浪费大量时间到最后就是一个求逆序对= =

代码

(敲了40分的暴搜BFS。

#include<bits/stdc++.h>
using namespace std;

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define LL long long

void Rd(int &res){
    char c;res=0;
    while((c=getchar())<48);
    do res=(res<<3)+(res<<1)+(c^48);
    while((c=getchar())>47);
}

#define M 1000004
int n,m,A[M];
struct P40{
    int P[14];
    int Dis[44444],id,s,t;
    map<int,int>ID;
    int Re[44444];
    int Hash(int *P){
        int h=0;
        REP(i,1,n+1)h=(h<<3)+(h<<1)+P[i];
        return h;
    }
    int Array(int x,int *P){
        DREP(i,n,0)P[i]=x%10,x/=10;
    }
    int Q[44444],l,r;
    int BFS(){
        Dis[s]=1;
        l=r=0;
        Q[r++]=s;
        while(l<r){
            int u=Q[l++];
            if(u==t)return Dis[u]-1;

            int x=Re[u];

            Array(x,P);

//          cerr<<Dis[u]<<":"<<endl;
//          REP(i,1,n+1)cerr<<P[i]<<' ';cerr<<endl;

            REP(i,1,n){
                swap(P[i],P[i+1]);
                int v=ID[Hash(P)];
                if(!Dis[v]) Dis[v]=Dis[u]+1,Q[r++]=v;
                swap(P[i],P[i+1]);
            }

        }
    }
    void Solve(){
        REP(i,1,n+1)P[i]=i;
        t=Hash(P);
        do{
            int h=Hash(P);
            ID[h]=++id,Re[id]=h;
        }while(next_permutation(P+1,P+n+1));

        t=ID[t];
        s=ID[Hash(A)];
//      cerr<<s<<","<<t<<endl;
        printf("%d\n",BFS());
    }
}P40;
struct P100{
    int Ans,B[M];
    void Merge(int l,int r){
        if(l>=r)return;
        int mid=l+r>>1;
        Merge(l,mid);
        Merge(mid+1,r);
        int i=l,j=mid+1,k=l;
        while(i<=mid && j<=r){
            if(A[i]<=A[j])B[k++]=A[i++];
            else Ans+=mid-i+1,B[k++]=A[j++];
        }
        while(i<=mid)B[k++]=A[i++];
        while(j<=r)B[k++]=A[j++];
        REP(v,l,r+1)A[v]=B[v];
    }
    void Solve(){
        Ans=0;
        Merge(1,n);
        printf("%d\n",Ans);
    }
}P100;
int main(){
    freopen("draw.in","r",stdin);
    freopen("draw.out","w",stdout);
    Rd(n),Rd(m);
    REP(i,1,n+1)A[i]=i;
    REP(i,0,m){
        int x;
        Rd(x);
        swap(A[x],A[x+1]);
    }
    REP(i,1,n+1)printf("%d%c",A[i]," \n"[i==n]);
//  if(n<=8)P40.Solve();
    P100.Solve();
    return 0;
}

T2 run

分析

先对整棵树求一个欧拉序。
那么每个节点就对应一段区间内的不断循环。
然后经过思考发现:
如果在第一次循环内未遇上当前点,那么以后都不会遇上。
那么每次记录到达当前的点的时间,
以及该点是否可用(为起始节点的祖先。
在欧拉序上标记,查询每个被访问到节点的若干时间前是否为可用节点即可。
然后Ans++。

代码

比赛中dfn没有清空导致数组访问越界,变成了40分= =

#include<bits/stdc++.h>
using namespace std;

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)

void Rd(int &res){
    char c;res=0;
    while((c=getchar())<48);
    do res=(res<<3)+(res<<1)+(c^48);
    while((c=getchar())>47);
}

int T,n;
#define M 500004
int Next[M],V[M],Head[M],tot,Tmp[M];
void Add_Edge(int u,int v){
    Next[++tot]=Head[u],V[Head[u]=tot]=v;
}
#define LREP(i,A) for(int i=Head[A];i;i=Next[i])
int DFN[M],P[M<<1],dfn;
int Fa[M];
void DFS(int A){
    DFN[A]=++dfn;
    P[dfn]=A;
    LREP(i,A){
        Fa[V[i]]=A;
        DFS(V[i]);
        P[++dfn]=A;
    }
}
struct P100{
    int x,Tim[M];
    bool Mark[M<<1];
    void Solve(){
        memset(Tim,-1,sizeof(Tim));
        memset(Mark,0,sizeof(Mark));
        Rd(x);
        int tim=0,Ans=0;
        while(x){
            Tim[x]=tim;Mark[DFN[x]]=1;
            x=Fa[x];tim++;
        }
        REP(i,1,dfn+1){
            if(Tim[P[i]]==-1)continue;
            int pos=i-Tim[P[i]];
            if(pos<=0)continue;
            if(Mark[pos])
                Ans++,Mark[pos]=0;
        }
        printf("%d\n",Ans);
    }
}P100;
int main(){
    Rd(T);
    while(T--){
        memset(Head,tot=dfn=0,sizeof(Head));
        Rd(n);
        REP(i,1,n+1){
            int p;
            Rd(p);
            REP(j,0,p)Rd(Tmp[j]);
            DREP(j,p-1,-1)Add_Edge(i,Tmp[j]);
        }
        DFS(1);
        P100.Solve();
    }
    return 0;
}

T3 tri

分析
P20

直接模拟

P40

然后发现Sum值是 Cn+1,1 Cn+1,n+1
于是处理出阶乘和逆元可在 O(nlogm) 内直接计算答案解决。

P50

m=1 时可找规律得到答案为 n2n1

P60

m=2 时可找规律得到答案为 n(n+1)2n2

P95

看起来上面很有规律。
然而 m=3 时答案为 nn(n+3)2n3
???
所以没有规律
然后要考虑其组合上的意义。
在n个颜色中取出i个颜色,然后作一个长度为m的序列,颜色可重。
可以发现序列的长度较小,
所以枚举长度为m的序列中颜色的个数。
然后容斥算答案。
Fi=i1j=0Ciji(ij)m(j%2?1:1)
Ans=min(n,m)i=1FiCin2i
复杂度为 O(m2)

P100

将上面这个改写为第二类斯特林数的形式。
Ans=min(n,m)i=1S(m,i)i!
NTT优化求出 S(m,1)S(m,m)
然后我不会。

代码

只有95分。

#include<bits/stdc++.h>
using namespace std;

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)

void Rd(int &res){
    char c;res=0;
    while((c=getchar())<48);
    do res=(res<<3)+(res<<1)+(c^48);
    while((c=getchar())>47);
}

#define Mod 998244353
int n,m;
int Pow(int x,int p){
    int Res=1,k=x;
    while(p){
        if(p&1)Res=1ll*Res*k%Mod;
        k=1ll*k*k%Mod;p>>=1;
    }
    return Res;
}
int C(int n,int i,int Inv){
    int Res=1;
    REP(a,n-i+1,n+1)
        Res=1ll*Res*a%Mod;
    return 1ll*Res*Inv%Mod;
}
struct P40{
    static const int M=1000004;
    int Fac[M],Inv[M];
    void Solve(){
        Fac[0]=Inv[0]=1;
        REP(i,1,n+1)Fac[i]=1ll*Fac[i-1]*i%Mod,Inv[i]=Pow(Fac[i],Mod-2);
        int Ans=0;
        REP(i,1,n+1) (Ans+=1ll*Inv[i]*Inv[n-i]%Mod*Pow(i,m)%Mod)%=Mod;
        printf("%d\n",1ll*Ans*Fac[n]%Mod);
    }
}P40;
struct P95{
    static const int M=3004;
    int Fac[M],Inv[M],Pw[M],Ans;
    void Solve(){
        Fac[0]=Inv[0]=1;
        REP(i,1,m+1){
            Fac[i]=1ll*Fac[i-1]*i%Mod;
            Inv[i]=Pow(Fac[i],Mod-2);
            Pw[i]=Pow(i,m);
        }
        Ans=0;
        REP(i,1,min(n,m)+1){
            int f=1,Res=0;
            DREP(j,i,0){
                (Res+=1ll*f*Fac[i]*Inv[j]%Mod*Inv[i-j]%Mod*Pw[j]%Mod)%=Mod;
                f=-f;
            }
            Res=1ll*Res*C(n,i,Inv[i])%Mod*Pow(2,n-i)%Mod;
            (Ans+=Res)%=Mod;
        }
        (Ans+=Mod)%=Mod;
        printf("%d\n",Ans);
    }
}P95;
int main(){
    Rd(n),Rd(m);
    if(m==0) printf("%d\n",Pow(2,n)-1);
    else if(n<=1000000)P40.Solve();
    else P95.Solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值