18年10月30日 NOIP模拟赛

T1 jkl

题解

显然每次都取a[i]的最大值/最小值,并更新a[i]即可

用数据结构维护这一操作。。得分看常数

事实上用v[i]记录权值为i的个数,然后for乱搞就可以了。。。

其它乱搞做法能获得不同的分数

提供一种50分解法

排序后

最小值,从左依次取到0

最大值,一直取最右的那个,如果它变得比前面的小就交换位置。。。

#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m;
int a[1000005],t[1000005];
long long mn,mx;
int main()
{
    m=read();n=read();
    for(int i=1;i<=m;i++)a[i]=read();
    sort(a+1,a+m+1);
    for(int i=1;i<=m;i++)t[i]=a[i];
    int now=1;
    for(int i=1;i<=n;i++)
    {
        mn+=t[now];
        t[now]--;if(!t[now])now++;
    }
    for(int i=1;i<=m;i++)
        t[i]=a[i];
    for(int i=1;i<=n;i++)
    {
        mx+=t[m];
        t[m]--;
        int k=m;
        while(t[k]

AC做法

#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m;
int a[1000005];
priority_queue q;
ll mx,mn;
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++)a[i]=read(),q.push(a[i]);
    sort(a+1,a+n+1);
    int now=1;
    for(int i=1;i<=m;i++)
    {
        int t=q.top();q.pop();
        mx+=t;
        q.push(t-1);
        while(!a[now])now++;
        mn+=a[now];a[now]--;
    }
    printf("%lld %lld",mx,mn);
    return 0;
}

T2 walk

题解

第一种做法直接暴力找循环节。。。

可以得70分

正解裸倍增

\(f[k,i]\)代表从i开始走2^k步会到哪里,初始\(f[0,i]=next[i]\)

\(f[k,i]=f[k-1,f[k-1,i]]\),复杂度\(m*log(n)\)

暴力(我的代码)

#include
using namespace std;

inline long long read()
{
    long long x = 0,t = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') t = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x*10 + ch -'0'; ch = getchar();}
    return x*t; 
}

int n,m,num[100010];

inline void init()
{
    n = read(); m = read();
    for(int i = 1;i <= n;++i)
    {
        num[i] = read();
    }
}

inline void greed()
{
    for(int i = 1;i <= m;++i)
    {
        int t = read();
        long long k = read();
        int end,cnt=0;
        int vis[100010];
        
        for(int i=1;i<=n;i++)vis[i]=0;
        
        for(int i=t;!vis[i];i=num[i])
        {
            vis[i]=1;
            end=num[i];
        }
        
        while(k&&t!=end)t=num[t],k--;
        
        if(k)
        {
            cnt=1;
            while(num[end]!=t) cnt++,end=num[end];
            k%=cnt;
        }
        while(k--)t=num[t];
        
        printf("%d\n",t);
    }
}

int main(int argc, char const *argv[])
{
    init();
    greed();
    return 0;
}

AC做法

#include
#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
ll bin[65];
int n,m;
int to[100005][65];
int main()
{
    n=read();m=read();
    bin[0]=1;for(int i=1;i<=60;i++)bin[i]=bin[i-1]<<1;
    for(int i=1;i<=n;i++)to[i][0]=read();
    for(int i=1;i<=60;i++)
        for(int j=1;j<=n;j++)
            to[j][i]=to[to[j][i-1]][i-1];
    for(int i=1;i<=m;i++)
    {
        int x=read();ll k=read();
        for(int t=0;t<=60;t++)if(k&bin[t])x=to[x][t];
        printf("%d\n",x);
    }
    return 0;
}

T3 mokou

题解

题目大意

给定一个无向有权图,首先一个最小生成树 MST,从 MST 中选取一个度数大于 1 的点 作为根 K,使每颗子树及该子树到根的边权之和方差最小。输出 K 和最小方差的值。

考察算法最小生成树 树的遍历

算法1

首先毫无疑问的需要用到求最小生成树的算法,我们考虑使用 Kruskal 算法或是Prim 算法。求出最小生成树以后,依次枚举每一个点作为根进行遍历,取出其中的最小方差即可。

时间复杂度:\(O(MlogM+N^2)\)

期望得分:60

算法2

由于后 40%的数据 N 比较大,所以只能通过 Kruskal 算法求出最小生成树,接下来任选一个点作为根,进行一次遍历。记录 w[i]表示以 i 点作为根的子树的边权之和。 然后依次枚举每一个点 i,该点的子树权值可以直接求出,而以它父亲作为根的子树需要特殊处理。这颗特殊子树的权值为最小生成树总权值减去该点权值 w[i]。然后计算出方差,最后选取所有点当中最小方差的那个点即可。

时间复杂度:\(O(MlogM+N)\)

期望得分:100

暴力

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

#define MAXN 50009
#define MAXE 200009
#define pb(x) push_back(x)
#define mk(x, y) make_pair(x, y)

struct EDGE
{
    int u, v;
    double length;
}   edge[ MAXE ];
int N, edgecnt;

struct Tree
{
    vector< pair > fir[ MAXN ];
    double Sum, w[ MAXN ];
    bool vis[ MAXN ];

    void Addedge(int u, int v, double length)
    {
        Sum += length;
        fir[u].pb( mk(v, length) );
        fir[v].pb( mk(u, length) );
        return ;
    }

    void DFS(int now)
    {
        vis[ now ] = true;
        w[ now ] = 0;
        for (int i = 0; i != fir[now].size(); i++)
            if (!vis[ fir[now][i].first ])
            {
                DFS( fir[now][i].first );
                w[now] += fir[now][i].second + w[ fir[now][i].first ];
            }
        return ;
    }

    void GetAns()
    {
        int best(-1);
        double best_ans(0), tp(0), Avg(0);
        for (int i = 1; i <= N; i++)
            if (fir[i].size() > 1)
            {
                memset(vis, 0, sizeof(vis));
                DFS(i);
                tp = 0;
                Avg = Sum / fir[i].size();
                for (int j = 0; j != fir[i].size(); j++)
                    tp += (Avg - fir[i][j].second - w[ fir[i][j].first ]) * (Avg - fir[i][j].second - w[ fir[i][j].first ]);
                if (best == -1 || tp < best_ans)
                    best_ans = tp, best = i;
            }
        printf("%d\n", best);
        return ;
    }
}   MST;

struct Kruskal
{
    int path[ MAXN ];

    int Find(int x)
    {
        if (x != path[x]) path[x] = Find( path[x] );
        return path[x];
    }

    void Input()
    {
        scanf("%d %d", &N, &edgecnt);
        for (int i = 1; i <= edgecnt; i++)
            scanf("%d %d %lf", &edge[i].u, &edge[i].v, &edge[i].length);
        return ;
    }

    void Work()
    {
        int cnt(1), x, y;
        for (int i = 1; i <= N; i++)
            path[i] = i;
        for (int i = 1; i <= edgecnt && cnt < N; i++)
        {
            x = Find( edge[i].u );
            y = Find( edge[i].v );
            if (path[x] == path[y]) continue;
            path[x] = path[y];
            MST.Addedge(edge[i].u, edge[i].v, edge[i].length);
            ++cnt;
        }
        return ;
    }
}   Kruskal;

bool Comp(EDGE x, EDGE y)
{
    return x.length < y.length;
}

int main()
{

    Kruskal.Input();
    sort(edge + 1, edge + edgecnt + 1, Comp);
        Kruskal.Work();
    MST.GetAns();

    return 0;
}

AC做法

#include 
#include 
#include 
#include 
#include 
using namespace std;

#define MAXN 50009
#define MAXE 200009
#define pb(x) push_back(x)
#define mk(x, y) make_pair(x, y)

struct EDGE
{
    int u, v;
    double length;
}   edge[ MAXE ];
int N, edgecnt;

struct Tree
{
    vector< pair > fir[ MAXN ];
    double Sum, w[ MAXN ];
    int path[ MAXN ];
    bool vis[ MAXN ];

    void Addedge(int u, int v, double length)
    {
        Sum += length;
        fir[u].pb( mk(v, length) );
        fir[v].pb( mk(u, length) );
        return ;
    }

    void DFS(int now)
    {
        vis[ now ] = true;
        for (int i = 0; i != fir[now].size(); i++)
            if (!vis[ fir[now][i].first ])
            {
                path[ fir[now][i].first ] = now;
                DFS( fir[now][i].first );
                w[now] += fir[now][i].second + w[ fir[now][i].first ];
            }
        return ;
    }

    void GetAns()
    {
        int best(-1);
        double best_ans(0), tp(0), Avg(0);
        DFS(1);
        for (int i = 1; i <= N; i++)
            if (fir[i].size() > 1)
            {
                tp = 0;
                Avg = Sum / fir[i].size();
                for (int j = 0; j != fir[i].size(); j++)
                    if (fir[i][j].first != path[i])
                        tp += (Avg - fir[i][j].second - w[ fir[i][j].first ]) * (Avg - fir[i][j].second - w[ fir[i][j].first ]);
                    else tp += (Avg - (Sum - w[i])) * (Avg - (Sum - w[i]));
                if (best == -1 || tp < best_ans)
                    best_ans = tp, best = i;
            }
        printf("%d\n", best);
        return ;
    }
}   MST;

struct Kruskal
{
    int path[ MAXN ];

    int Find(int x)
    {
        if (x != path[x]) path[x] = Find( path[x] );
        return path[x];
    }

    void Input()
    {
        scanf("%d %d", &N, &edgecnt);
        for (int i = 1; i <= edgecnt; i++)
            scanf("%d %d %lf", &edge[i].u, &edge[i].v, &edge[i].length);
        return ;
    }

    void Work()
    {
        int cnt(1), x, y;
        for (int i = 1; i <= N; i++)
            path[i] = i;
        for (int i = 1; i <= edgecnt && cnt < N; i++)
        {
            x = Find( edge[i].u );
            y = Find( edge[i].v );
            if (path[x] == path[y]) continue;
            path[x] = path[y];
            MST.Addedge(edge[i].u, edge[i].v, edge[i].length);
            ++cnt;
        }
        return ;
    }
}   Kruskal;

bool Comp(EDGE x, EDGE y)
{
    return x.length < y.length;
}

int main()
{

    Kruskal.Input();
    sort(edge + 1, edge + edgecnt + 1, Comp);
    Kruskal.Work();
    MST.GetAns();

    return 0;
}

转载于:https://www.cnblogs.com/Chicago/p/9920669.html

你可能感兴趣的:(18年10月30日 NOIP模拟赛)