• 18年10月30日 NOIP模拟赛


    T1 jkl

    题解

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

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

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

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

    提供一种50分解法

    排序后

    最小值,从左依次取到0

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

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    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]<t[k-1])swap(t[k],t[k-1]),k--;
        }
        printf("%lld %lld",mx,mn);
        return 0;
    }
    

    AC做法

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    #include<queue>
    #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<int> 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<bits/stdc++.h>
    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
    ",t);
    	}
    }
    
    int main(int argc, char const *argv[])
    {
        init();
        greed();
        return 0;
    }
    
    

    AC做法

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    #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
    ",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 <iostream>
    #include <vector>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <algorithm>
    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<int, double> > 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
    ", 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 <iostream>
    #include <vector>
    #include <stdio.h>
    #include <stdlib.h>
    #include <algorithm>
    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<int, double> > 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
    ", 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;
    }
    
  • 相关阅读:
    企业局域网的组建
    安装网吧收费系统
    网吧游戏的三层更新
    Java4Android之BlockingQueue
    阿里员工离职潮的背后?
    使用SharePoint管理中心管理服务
    获取联系人【自己定义布局文件与主布局文件相连,数据库内容查找并显示】
    读取两文件,不同的内容存入还有一个文件里
    graph driver-device mapper-01driver初始化
    .net读取异步Post的内容
  • 原文地址:https://www.cnblogs.com/Chicago/p/9920669.html
Copyright © 2020-2023  润新知