2017.10.16离线赛总结

draw ——3787

思路:这是源自一个岛国的游戏…
思考一下画鬼脚的本质,一条竖线其实就是一次对相邻两个元素的交换操作。
所以,模拟画鬼脚的时候,只需要按照高度从高到低,依次进行所有的交换操作即可。
那么,对于第二问,众所周知,就是求排列的逆序对数——线段树或BIT

#include<bits/stdc++.h>
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--) 
#define INF 0x3f3f3f3f
#define LL long long
#define N 100005
#define M 1000005
#define INF 0x3f3f3f3f
using namespace std;
int n,m,ans;
int pos[N];
struct BIT{
    int A[N];
    void add(int x,int v){
        while(x<=N){
            A[x]+=v;
            x+=x&-x;
        }
    }
    int query(int x){
        int res=0;
        while(x){
            res+=A[x];
            x-=x&-x;
        }
        return res;
    }
}BIT;
int main(){
    scanf("%d%d",&n,&m);
    REP(i,1,n)pos[i]=i;
    REP(i,1,m){
        int x;scanf("%d",&x);
        swap(pos[x],pos[x+1]);
    }
    REP(i,1,n)printf("%d ",pos[i]);
    DREP(i,n,1){
        ans+=BIT.query(pos[i]);
        BIT.add(pos[i],1);
    }
    printf("\n%d\n",ans);
    return 0;
}

run ——3788

思路:50%:直接模拟,链时,显然 他会消灭所有距离他为偶数条的祖先。
100%:先dfs,遇到一个点就把这个点记录到一个数组,即求出了树的欧拉序,显然如果不考虑循环的话,guard是在这个序列上每次往后走一个,起始位置就是第i个点第一次出现的位置。而显然 YOUSIKI 只可能与他的所有祖先相遇,所以我们要最短路把树上的这些祖先的位置标记出来。
假设 YOUSIKI 现在走到了 x 点,过了 t 秒,那么我们在这个序列上遍历 x 出现的所有位置,并查看这个位置往前 t 个是否为 x 的祖先,如果是,把那个祖先标为1,表示已被消灭。
复杂度 O(n)。

#include<bits/stdc++.h>
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--) 
#define INF 0x3f3f3f3f
#define LL long long
#define N 500005
#define INF 0x3f3f3f3f
using namespace std;
int n,m;

void Rd(int &ans){
    ans=0;char c;
    while((c=getchar())<48);
    do ans=(ans<<1)+(ans<<3)+(c^48);
    while((c=getchar())>47);
}
struct P100{
    int n,T,ans;
    int fa[N],Lt[N<<1], mark[N<<1];
    vector<int>E[N],es[N];
    void Init(){
        memset(fa,0,sizeof(fa));
        memset( mark,0,sizeof( mark));
        memset(Lt,0,sizeof(Lt));
        REP(i,1,n)E[i].clear(),es[i].clear();
        T=0;ans=0;
    }
    void dfs(int x,int f){
        Lt[x]=++T;
        fa[x]=f;
        es[x].push_back(T);
        REP(i,0,E[x].size()-1){
            int y=E[x][i];
            dfs(y,x);
            es[x].push_back(++T);
        }
    }
    void solve(){
        Init();
        Rd(n); 
        REP(i,1,n){
            int k,x;
            Rd(k);
            REP(j,1,k)Rd(x),E[i].push_back(x);
        }
        int beg;Rd(beg);
        dfs(1,0);       
        for(int i=beg;i;i=fa[i])mark[Lt[i]]=1;
        int t=0;
        for(int i=beg;i;i=fa[i]){
            REP(j,0,es[i].size()-1){
                int dt=es[i][j]-t;
                if(dt<=0 || ! mark[dt])continue;
                ans++,mark[dt]=0;
            } 
            t++;
        }
        printf("%d\n",ans); 

    }
}AC;
int main(){
    int Case;
    Rd(Case);
    while(Case--)AC.solve();
    return 0;
}

tri ——3789

思路:
5%:直接输出2^n-1;
20%:强模拟;
40%:推出等式,与组合有关——∑C(i,x)(i=0 to n-1) = C(n,x+1)
m=1时:可以推出通项公式——2^(n-1)*n;
m=2时:也可以推出——n*(2^(n-1)+2^(n-2)*(n-1));
所以可以把问题转换到第 n+1 行上,相当于求 ∑C(n,i)*i^m (i=1 to n) ,O(n lg m);
95%:
考虑这个式子的组合意义
相当于先枚举 i,然后在 n 个颜色里选出 i 个颜色,再用这 i 个颜色可重复排出一个长度为m 的排列的方案数。
换个思路。
枚举最后排列中实际用了几种颜色(*C(n,i) ),其他的颜色可以选或不选( *2^(n-i)),然后再乘上 i 种颜色能排出的排列数。
现在问题在于排列数怎么求,这里可以用 dp 或容斥,说一下容斥。
首先 i 个颜色随便放的方案数是 i^m,但现在可能最终只出现了 i-1 种颜色,所以需要减去C(i,i − 1) ∗ (i − 1)^m,同理再加上 C(i,i − 2) ∗ (i − 2)m…O(m^2)。
可能有人已经发现这个东西就是 S(m,i) ∗ i!,S为第二类斯特林数,表示把 m 个物品放入 i个相同盒子且盒子非空的方案数,因为本题是一个排列,所以需要乘上i! 。
第二类斯特林数可以用 或容斥求,O(n^2) 。
将容斥的式子化简一下,发现是一个卷积的形式,用 NTT 可以求出 S(m, 1) 到S(m, m) , O(mlogm + mlogn),然而这5分太恶心了,有 AK 梦想的选手
可以写一写。

#include<bits/stdc++.h>
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--) 
#define INF 0x3f3f3f3f
#define LL long long
#define N 1000005
#define Mod 998244353
#define INF 0x3f3f3f3f
// LL mod 
using namespace std;
LL n,m;
LL dp[3005][3005];
void Init(){
    dp[0][0]=1;
    REP(i,1,n)
        REP(j,1,i)
            dp[i][j]=(dp[i-1][j]+dp[i-1][j-1])%Mod;
}
LL Pow(LL a,LL b){
    LL res=1;
    while(b>0){
        if(b&1)res*=a,res%=Mod;
        b>>=1,a*=a,a%=Mod;
    }
    return res;
}
struct p20{
    LL ans;
    void solve(){
        Init();
        REP(j,1,n){
            LL p=Pow(j,m);
            REP(i,j,n)dp[i][j]*=p,dp[i][j]%=Mod,ans+=dp[i][j],ans%=Mod;
        }
        printf("%lld\n",ans);
    }
}p20;
struct p40{
    void solve(){
        LL C=n;
        LL ans=n;
        REP(i,1,n){
            C=(C*(n-i)%Mod)*Pow(i+1,Mod-2)%Mod;
            ans+=C*Pow(i+1,m)%Mod,ans%=Mod;
        }
        printf("%lld\n",ans);
    }
}p40;
struct p95{
    LL fac[3005];
    void Init(){
        dp[0][0]=1;
        REP(i,1,3005){
            dp[i][0]=1;
            REP(j,1,i)dp[i][j]=(dp[i-1][j-1]+dp[i-1][j])%Mod;
        }
        fac[0]=fac[1]=1;// i!
        REP(i,2,3005)fac[i]=(i*fac[i-1])%Mod;
    }
    void solve(){
        Init(); 
        LL ans=0,p=n;
        REP(i,1,m){
            LL f=-1,sum=Pow(i,m);
            DREP(j,i-1,0)sum+=f*dp[i][j]*Pow(j,m)%Mod,sum%=Mod,f=-f;//rong chi
            ans+=p*Pow(2,n-i)%Mod*sum%Mod,ans%=Mod;
            p=p*(n-i)%Mod*Pow(i+1,Mod-2)%Mod;
        }
        printf("%lld\n",ans%Mod);
    }
}p95;
int main(){
//  freopen("tri.in","r",stdin);
//  freopen("tri.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    if(!m)printf("%lld\n",Pow(2,n)-1);
    else if(m==1)printf("%lld\n",((Pow(2,n-1)%Mod)*n)%Mod);
    else if(m==2)printf("%lld\n",((Pow(2,n-1)%Mod+((Pow(2,n-2)%Mod)*(n-1))%Mod)*n)%Mod);
    else if(n<=2000)p20.solve();
    else if(n<=1000000)p40.solve();
    else if(m<=3000)p95.solve();
    return 0;
}

小结:今天的题着实有点骚,但有新意,还是可以,只是今天第3题的暴力都没打…第2题写着写着就内心崩溃了…看到第3题(woc,第3题数论题,数论是什么…)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值