10.24考試題解qwq

考點難度都很合適的一套題目,大概在day1到day2以前

T1

猴猴最喜歡在樹上玩耍,一天猴猴又跳上了一棵樹,這棵樹有N個蘋果,每一個蘋果有一個編號,分
別爲0~N-1,它們之間由N-1個樹枝相連,猴猴能夠從樹枝的一端爬到樹枝的另外一端,因此猴猴可
以從任意一個蘋果的位置出發爬到任意猴猴想去的蘋果的位置。猴猴開始在編號爲K的蘋果的位
置,而且把這個蘋果吃了,以後每一天猴猴都要去吃一個蘋果,可是樹上那麼多蘋果吃哪一個呢?
猴猴想到本身去吃蘋果時必定會把路上遇到的蘋果都吃掉,因而猴猴決定去吃能讓本身這天吃的
蘋果數量最多的那個蘋果,若是有多個蘋果知足條件,猴猴就會去吃這些中編號最小的蘋果,那
麼猴猴會按照什麼順序吃蘋果呢?c++

對於30%的數據:N<=100
對於60%的數據:N<=1000
對於100%的數據:N<=50000,0<=K<N
git

分析:60%的分數都只要暴力模擬就行了,每次找最長的走過去;數組

考慮一個性質:走過的點的權值會消失,那麼每次移動到終點位置和每次都從初始給定的點出發的答案都同樣的,並且終點必定會是葉子節點;ide

那麼咱們設初始節點爲根節點,先進行一遍dfs,把全部葉子節點記錄下來,按照深度排序;spa

而後讓每一個點沿着父親往上跳,每跳一步答案增長1,遇到根節點或者走過的節點就中止(遇到走過的點說明上面的權值都沒有了code

而後把葉子節點按答案從小到大輸出(看完題解以爲智商被人按在地上摩擦了)blog

#include<bits/stdc++.h>
using namespace std;
namespace knife_rose{
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
    inline int read()
    {
        int x=0;char ch,f=1;
        for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
        if(ch=='-') f=0,ch=getchar();
        while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
        return f?x:-x;
    }
    const int N=1e5+10;
    int n,rt;
    int head[N],cnt;
    struct point
    {
        int nxt,to;
        point(){}
        point(const int &nxt,const int &to):nxt(nxt),to(to){}
    }a[N<<1];
    inline void link(int x,int y)
    {
        a[++cnt]=(point){head[x],y};head[x]=cnt;
        a[++cnt]=(point){head[y],x};head[y]=cnt;
    }
    int f[N],dep[N],lea[N],num,sum[N];
    bool vis[N];
    inline void dfs(int now,int fa)
    {
        f[now]=fa;
        dep[now]=dep[fa]+1;
        bool flag=0;
        for(int i=head[now];i;i=a[i].nxt)
        {
            int t=a[i].to;
            if(t==fa) continue;
            flag=1;
            dfs(t,now);
        }
        if(!flag) lea[++num]=now;
    }
    inline bool cmp1(int a,int b)//深度排序,深度相同按編號大小
    {
        return dep[a]^dep[b]?dep[a]>dep[b]:a<b;
    }
    inline bool cmp2(int a,int b)//答案排序
    {
        return sum[a]^sum[b]?sum[a]>sum[b]:a<b;
    }
    signed main()
    {
        n=read(),rt=read();
        for(int x,i=1;i<n;++i)
        {
            x=read();
            link(x,i);
        }
        dfs(rt,rt);
        sort(lea+1,lea+num+1,cmp1);
        vis[rt]=1;
        for(int i=1;i<=num;++i)
        {
            int now=lea[i];
            while(!vis[now])
            {
                ++sum[lea[i]];
                vis[now]=1;
                now=f[now];
            }
        }
        sort(lea+1,lea+num+1,cmp2);
        printf("%d\n",rt);
        for(int i=1;i<=num;++i) printf("%d\n",lea[i]);
    return 0;
    }
}
signed main()
{
    return knife_rose::main();
}
View Code

 

T2

猴猴最愛吃香蕉了。天天猴猴出門都會摘不少不少的香蕉,每一個香蕉都有一個甜度,猴猴不必定
要把全部的香蕉都吃掉,猴猴天天都有一個心情值K,猴猴但願當天吃的香蕉知足這麼一個條件,
這些香蕉的甜度乘積剛好等於K,可是猴猴並不知道有多少種方法,因而猴猴把這個問題交給你。
排序

對於30%的數據:n,K<=100
對於60%的數據:n<=1000,K<=10000
對於100%的數據:n<=1000,K<=100000000,D<=20(D是數據組數
get

 

30%:我也沒想出來咋拿30分(it

60%:題目是個很明顯的揹包,可是因爲是乘法因此有一些特殊之處:只有k的約數才能做爲轉移,因此咱們能夠篩出來k的約數,是根號量級的;

統計答案須要二分查找,複雜度O(D×n√k×log(√k))

100%:其實上面那個作法就是100分,然而官方題解也是這麼給的,可是很明顯複雜度不對啊qwq

然而機房裏某位巨佬想出了複雜度更優秀的作法,%%%szx

先對k進行質因數分解,根據每一個質因子個數將答案壓縮成一個多進制狀態,而後將每一個a[i]也壓縮成對應的多進制狀態進行轉移,能夠發現質因子個數不多,不超過30個

設t是多進制狀態最大值

複雜度爲O(√k+n√a[i]+nt

 

#include<bits/stdc++.h>
using namespace std;
namespace knife_rose{
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
    inline int read()
    {
        int x=0;char ch,f=1;
        for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
        if(ch=='-') f=0,ch=getchar();
        while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
        return f?x:-x;
    }
    const int N=1e4+10,p=1e9+7;
    int tyx,n,m,ed,tot;
    int a[N];
    int st[40],sum[40],top;
    int git[40],c[40],s[N][40];
    int g[N],f[1000010];
    bool vis[N];
    inline void clear()
    {
        memset(g,0,sizeof(g));
        memset(f,0,sizeof(f));
        memset(sum,0,sizeof(sum));
        memset(s,0,sizeof(s));
        memset(vis,0,sizeof(vis));
        top=0;
    }
    signed main()
    {
        tyx=read();
        while(tyx--)
        {
            clear();
            n=read(),m=read();
            for(int i=1;i<=n;++i) a[i]=read();
            int qr=sqrt(m),tmp=m;
            for(int i=2;i<=qr;++i)
            {
                if(tmp%i==0)
                {
                    st[++top]=i;
                    while(tmp%i==0) ++sum[top],tmp/=i;//質因數分解
                }
            }
            if(tmp^1) st[++top]=tmp,sum[top]=1;//加上大質因子
            git[1]=1;
            for(int i=1;i<=top;++i)
                git[i+1]=git[i]*(sum[i]+1);//git[i]表示,多進制狀態下,第i位一個1表明十進制多少
            ed=0;
            for(int i=1;i<=top;++i) ed+=git[i]*sum[i];//ed是最終狀態
            for(int i=1;i<=n;++i)
            {
                if(m%a[i]){vis[i]=1;continue;}//不是M的約數沒用
                qr=sqrt(a[i]),tot=0;
                for(int j=2;j<=qr;++j)
                {
                    if(a[i]%j==0)
                    {
                        c[++tot]=j;
                        while(a[i]%j==0) ++s[i][tot],a[i]/=j;//質因數分解*2
                    }
                }
                if(a[i]^1) c[++tot]=a[i],s[i][tot]=1;
                int t=1;
                for(int j=1;j<=top;++j)
                {
                    while(c[t]<st[j]&&t<tot) ++t;
                    if(c[t]==st[j]) g[i]+=git[j]*s[i][t];//g[i]爲a[i]對應的多進制狀態數
                }
            }
            f[0]=1;
            for(int t,now,flag,i=1;i<=n;++i)
            {
                if(vis[i]) continue;
                for(int j=ed;~j;--j)
                {
                    flag=0;
                    t=0;
                    for(int k=1;k<=top;++k)
                    {
                        now=(j%git[k+1])/git[k];//now是a[i]對應的g[i]在第i位有多大
                        if(now+(g[i]%git[k+1])/git[k]>sum[k]){flag=1;break;}//若是第i位當前數字+g[i]的第i位數字大於上界取消
                        t+=(now+(g[i]%git[k+1])/git[k])*git[k];
                    }
                    if(flag) continue;
                    f[t]+=f[j];
                    if(f[t]>=p) f[t]-=p;
                }
            }
            printf("%d\n",f[ed]);
        }
    return 0;
    }
}
signed main()
{
    return knife_rose::main();
}
/*
1
10 10
1 5 1 2 4 5 1 2 1 5

*/
View Code

 

T3

猴猴今天要和小夥伴猩猩比賽爬樹,爲了公平不碰撞,猴猴和猩猩須要在不一樣的樹上攀爬。因而
它們選了兩顆節點數同爲n的樹,並將兩棵樹的節點分別以1~n標號(根節點標號爲1),但兩棵樹
的節點鏈接方式不盡相同。
如今它們決定選擇兩個標號的點進行比賽。爲了方便統計,規定它們比賽中必須都向上爬。(即
選定的賽段節點u→節點v都必須指向葉子方向)請你求出這兩棵樹上共有多少對節點知足比賽的需
求。

對於30%的數據:n≤1000
對於50%的數據:n≤10000
對於100%的數據:n≤100000,1≤a,b≤n

題意大概是選一對點使得在這兩顆樹上都知足某個點是另外一個點的祖先

30%:記錄每一個點是否是在兩棵樹上都是本身祖先,枚舉點對

50%:???好像沒啥辦法

100%:其實這個題目就是一個樹上逆序對問題,咱們考慮先對第一顆樹以dfs序爲下標建樹狀數組,再遍歷第二棵樹,每次進去的先把本身放進樹狀數組,而後求一下當前子樹範圍內點的個數,出來的時候再求一遍,兩次結果做差就是知足題目要求的點對(進去的時候不存在而回溯的時候存在說明既是第一顆樹的子樹也是第二棵樹的子樹)

 

#include<bits/stdc++.h>
using namespace std;
namespace knife_rose{
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
    inline int read()
    {
        int x=0;char ch,f=1;
        for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
        if(ch=='-') f=0,ch=getchar();
        while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
        return f?x:-x;
    }
    const int N=1e5+10;
    int n,ret;
    int head[N],cnt;
    struct point
    {
        int nxt,to;
        point(){}
        point(const int &nxt,const int &to):nxt(nxt),to(to){}
    }a[N<<1];
    inline void link(int x,int y)
    {
        a[++cnt]=(point){head[x],y};head[x]=cnt;
        a[++cnt]=(point){head[y],x};head[y]=cnt;
    }
    int st[N],ed[N],idx;
    inline void dfs1(int now,int fa)
    {
        st[now]=++idx;
        for(int i=head[now];i;i=a[i].nxt)
        {
            int t=a[i].to;
            if(t==fa) continue;
            dfs1(t,now);
        }
        ed[now]=idx;
    }
    int tr[N];
    inline int lowbit(int i)
    {
        return i&-i;
    }
    inline void update(int x,int k)
    {
        for(int i=x;i<=n;i+=lowbit(i))
            tr[i]+=k;
    }
    inline int query(int y)
    {
        int ret=0;
        for(int i=y;i;i^=lowbit(i))
            ret+=tr[i];
        return ret;
    }
    inline void dfs2(int now,int fa)//樹上逆序對
    {
        int ans=query(ed[now])-query(st[now]-1);
        for(int i=head[now];i;i=a[i].nxt)
        {
            int t=a[i].to;
            if(t==fa) continue;
            dfs2(t,now);
        }
        ret+=query(ed[now])-query(st[now]-1)-ans;
        update(st[now],1);
    }
    signed main()
    {
        n=read();
        for(int x,y,i=1;i<n;++i)
        {
            x=read(),y=read();
            link(x,y);
        }
        dfs1(1,0);
        memset(head,0,sizeof(head));
        cnt=0;
        for(int x,y,i=1;i<n;++i)
        {
            x=read(),y=read();
            link(x,y);
        }
        dfs2(1,0);
        printf("%d\n",ret);
    return 0;
    }
}
signed main()
{
    return knife_rose::main();
}
View Code
相關文章
相關標籤/搜索