• 【解题报告】NOIP2018


    【解题报告】NOIP2018

    她开始变了……

    Day1

    T1 铺设道路

    思路

    CCF狠起来连自己都抄

    就是2013年的积木大赛么

    甚至都是春春幼儿园……

    #include <iostream>
    using namespace std;
    int main()
    {
        int n,a,last=0,ans=0;
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>a;
            if(a>last)ans+=(a-last);
            last=a;
        }
        cout<<ans<<endl;
    }
    

    T2 货币系统

    思路

    题意就是你有 \(n\) 个数字,然后你要将这些数字加起来,看有没有不能表示的数字

    然后看能不能用 \(m \le n\) 个数字使这些数字的不能表示的数字和原来用 \(n\) 个数字不能表示数字的集合是同一个集合

    我们考虑枚举

    自己肯定是有的

    我们对已有的钱数量进行排序,然后我们对它们进行枚举

    枚举某个钱数是否能搞到,前提是这个 \(i\) 的这个钱有

    然后我们就依次枚举这个钱和其他钱的组合

    有人说了,那不得枚举到正无穷去?

    不对,如果有不同的话,我们已经在一个最大的钱 \(money_n\) 的这个范围之内找出来的,而其他的都是和他属于大概是同余的关系了,所以我们只用枚举到最后一个钱那么大行了

    如果两个钱加起来超过最大的钱的话,我们就直接 break 掉就好了

    所以最后的代码就是枚举

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <string>
    using namespace std;
    int T;
    int ex[50005]={0},money[1005]={0};
    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<<1)+(x<<3)+ch-48;ch=getchar();}
    	return x*f;
    }
    int main()
    {
    	T=read();
    	while(T--)
    	{
    		int n=read();
    		memset(ex,0,sizeof(ex));
    		memset(money,0,sizeof(money));
    		for(int i=1;i<=n;i++)
    		{
    			money[i]=read();
    			ex[money[i]]=2;
    		}
    		sort(money+1,money+1+n);
    		for(int i=1;i<=money[n];i++)
    		{
    			if(ex[i]>0)
    			{
    				for(int j=1;j<=n;j++)
    				{
    					if(i+money[j]<=money[n])
    					ex[i+money[j]]=1;
    					else 
    					break;
    				}
    			}
    		}
    		int ans=0;
    		for(int i=1;i<=money[n];i++)
    		if(ex[i]==2)
    		ans++;
    		cout<<ans<<endl;
    	}
    	return 0;
    }
    

    T3 赛道修建

    思路

    思路沿用自 @first_fan

    简要题意就是

    给定一棵树的信息,求这棵树 \(m\) 条互不相交的链中的最小值的最大值可以是多少

    最小值最大,显然的二分

    我们可以知道,如果一条链,经过了一个结点 \(i\) 的父节点,同时包含了 \(i\) 到子节点的某条边,那么它就只能包含 \(i\) 到所有子节点中边的一条

    于是我们推出来,对于任意结点 \(i\) 和它的子节点 \(j,k\) 以及其中的一条链 \(i \rightarrow j\) ,我们有三种情况需要讨论

    • 不要
    • 我们用 \(j \rightarrow i \rightarrow k\) 的链
    • 我们用 \(f_i \rightarrow i \rightarrow j\) 的链

    所以,我们对于 \(i\) 尝试连边,然后如果连一个的话,我们就把新的边长向上传递,和父节点并成同一个链

    我们是在二分最小值,所以按照上面的步骤连边之后,如果链长大于 \(len\) 的话,我们就重新开一个新的链,如果链数不够的话,说明我们最小的还要更小

    我们每次传回的父节点一定要足够长

    所以有 \(len_{pre}+len_{now} \ge binary_{len}\)

    然后用 \(multiset\) 维护一下就好了

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <string>
    #include <set>
    #define int long long
    using namespace std;
    const int maxn=50005;
    struct edge{
    	int e,next,val;
    }ed[maxn<<1];
    int en,first[maxn];
    void add_edge(int s,int e,int val)
    {
    	en++;
    	ed[en].next=first[s];
    	first[s]=en;
    	ed[en].e=e;
    	ed[en].val=val;
    }
    int n,m,mid,ans;
    int f[maxn],g[maxn];
    multiset <int> s;
    multiset <int>::iterator it;
    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-48;ch=getchar();}
    	return x*f;
    }
    void init()
    {
    	memset(f,0,sizeof(f));
    	memset(g,0,sizeof(g));
    }
    void dfs(int x,int fa)
    {
    	for(int i=first[x];i;i=ed[i].next)
    	{
    		int e=ed[i].e;
    		if(e==fa) continue;
    		dfs(e,x);
    		f[x]+=f[e];
    	}
    	for(int i=first[x];i;i=ed[i].next)
    	{
    		int e=ed[i].e;
    		int val=ed[i].val;
    		if(e==fa) continue;
    		s.insert(g[e]+val);
    	}
    	while(s.size())
    	{
    		int now=*s.rbegin();
    		if(now>=mid)
    		{
    			it=s.find(now);
    			f[x]++;
    			s.erase(it);
    		}
    		else break;
    	}
    	while(s.size())
    	{
    		int now=*s.begin();
    		s.erase(s.begin());
    		it=s.lower_bound(mid-now);
    		if(it==s.end())
    		g[x]=now;
    		else 
    		{
    			f[x]++;
    			s.erase(it);
    		}
    	}
    }
    signed main()
    {
    	int l=0,r=1e8;
    	n=read(),m=read();
    	for(int i=1;i<=n-1;i++)
    	{
    		int x=read(),y=read(),z=read();
    		add_edge(x,y,z);
    		add_edge(y,x,z);
    		
    		l=min(l,z);
    		r+=z;
    	}
    	while(l<=r)
    	{
    		init();
    		mid=(l+r)>>1;
    		dfs(1,0);
    		if(f[1]>=m) ans=max(ans,mid),l=mid+1;
    		else r=mid-1;
    	}
    	cout<<ans<<'\n';
    	return 0;
    }
    

    Day2

    T1 旅行

    思路

    贪心的部分分有40pts,我们可以在10min内拿到

    我们考虑直接dfs,对于每个点找出它编号最小的儿子先遍历,这样就有40pts了

    但是没有考虑可以往回走,然后走到一个更小结点的情况,能有四十分已经很人性了

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <string>
    #include <queue>
    using namespace std;
    const int maxn=2005;
    struct edge{
    	int e,next;
    }ed[maxn<<1];
    int en,first[maxn];
    void add_edge(int s,int e)
    {
    	en++;
    	ed[en].next=first[s];
    	first[s]=en;
    	ed[en].e=e;
    }
    int n,m;
    int ans[maxn],now[maxn],tot;
    bool vis[maxn];
    void dfs(int x,int fa)
    {
    	priority_queue <int> q;
    	if(!vis[x])
    	{
    		ans[++tot]=x;
    		vis[x]=true;
    	}
    	
    	for(int i=first[x];i;i=ed[i].next)
    	{
    		int e=ed[i].e;
    		if(e==fa) continue;
    		q.push(-e);
    	}
    	while(q.size())
    	{
    		int e=-q.top();
    		q.pop();
    		dfs(e,x);
    	}
    }
    int main()
    {
    	cin>>n>>m;
    	for(int i=1;i<=m;i++)
    	{
    		int u,v;
    		cin>>u>>v;
    		add_edge(u,v);
    		add_edge(v,u);
    	}
    	dfs(1,0);
    	for(int i=1;i<=tot;i++)
    	cout<<ans[i]<<" ";
    	cout<<'\n';
    	return 0;
    }
    
    

    vector 储存然后提前排好序甚至可以60pts

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <string>
    #include <cstring>
    #include <vector>
    using namespace std;
    const int maxn=5005;
    int n,m;
    int ans[maxn],tot;
    bool vis[maxn];
    vector <int> g[maxn];
    void dfs(int x,int fa)
    {
    	if(vis[x]) return ;
    	vis[x]=true;
    	ans[++tot]=x;
    	for(int i=0;i<g[x].size();i++)
    	{
    		int e=g[x][i];
    		if(e==fa) continue;
    		dfs(e,x);
    	}
    }
    int main()
    {
    	cin>>n>>m;
    	for(int i=1;i<=m;i++)
    	{
    		int u,v;
    		cin>>u>>v;
    		g[u].push_back(v);
    		g[v].push_back(u);`
    	}
    	for(int i=1;i<=n;i++)
    	sort(g[i].begin(),g[i].end());
    	dfs(1,0);
    	for(int i=1;i<=tot;i++)
    	cout<<ans[i]<<" ";
    	cout<<'\n';
    	return 0;
    }
    

    然后暴力枚举删哪一条边,可以 \(O(n^2)\) 通过此题

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <string>
    #include <vector>
    #define int long long
    using namespace std;
    const int maxn=5005;
    int n,m;
    int ru,rv;
    int ans[maxn],tot,res[maxn];
    bool vis[maxn];
    bool v[maxn];
    vector <int> g[maxn];
    struct edge{
    	int p,q;
    }a[maxn<<1];
    
    void dfs(int x,int fa)
    {
    	if(vis[x]) return ;
    	vis[x]=true;
    	ans[++tot]=x;
    	for(int i=0;i<g[x].size();i++)
    	{
    		int e=g[x][i];
    		if(e==fa) continue;
    		dfs(e,x);
    	}
    }
    
    void dfs1(int x,int fa)
    {
    	if(vis[x]) return ;
    	vis[x]=true;
    	res[++tot]=x;
    	for(int i=0;i<g[x].size();i++)
    	{
    		int e=g[x][i];
    		if(e==fa) continue;
    		if((x==ru&&e==rv)||(x==rv&&e==ru))
    		continue;
    		dfs1(e,x);
    	}
    }
    
    bool check()
    {
    	for(int i=1;i<=n;i++)
    	{
    		if(res[i]<ans[i]) 
    		return true;
    		else if(res[i]>ans[i])
    		return false;
    	}
    	return false;
    }
    void cp()
    {
    	for(int i=1;i<=n;i++)
    	ans[i]=res[i];
    }
    signed main()
    {
    	cin>>n>>m;
    	for(int i=1;i<=m;i++)
    	{
    		int u,v;
    		cin>>u>>v;
    		g[u].push_back(v);
    		g[v].push_back(u);
    		a[i].p=u;
    		a[i].q=v;
    	}
    	
    	for(int i=1;i<=n;i++)
    	sort(g[i].begin(),g[i].end());
    	
    	if(n==m)
    	{
    		bool flag=true;
    		for(int i=1;i<=m;i++)
    		{
    			ru=a[i].p,rv=a[i].q;
    			memset(vis,false,sizeof(vis));
    			tot=0;
    			dfs1(1,0);
    			if(tot<n) continue;
    			if(flag)
    			{
    				cp();
    				flag=false;
    			}
    			if(check())
    			cp();
    		}
    	}
    	else 
    	dfs(1,0);
    	
    	for(int i=1;i<=n;i++)
    		cout<<ans[i]<<" ";
    	cout<<'\n';
    	
    	return 0;
    }
    
    

    其实我们可以找到环,然后再暴力断环上的边的,但我不想写了

    T2 填数游戏

    思路

    有人说用搜索,有人说用状压

    我说我啥都不会,那就打表找规律吧

    规律蕴含在代码之中

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <string>
    #define int long long 
    using namespace std;
    const int mod=1e9+7;
    int n,m;
    inline int ksm(int a,int b,int p)
    {
    	int res=1%p;
    	while(b)
    	{
    		if(b&1)
    		res=1ll*res%p*a%p;
    		a=1ll*a%p*a%p;
    		b>>=1;
    	}
    	return res%p;
    }
    signed main()
    {
    	cin>>n>>m;
    	if(n>m)
    	swap(n,m);
    	if(n==1)
    		cout<<ksm(2,m,mod)%mod<<'\n';
    	else if(n==2)
    		cout<<4*ksm(3,m-1,mod)%mod<<'\n';
    	else if(n==3)
    		cout<<112*ksm(3,m-3,mod)%mod<<'\n';
    	else
    	{
    		if(m==n)
    		cout<<((83*ksm(8,n,mod)%mod+5*ksm(2,n+7,mod)%mod)%mod*190104168%mod)%mod<<'\n';
    		else
    		cout<<((83*ksm(8,n,mod)%mod+ksm(2,n+8,mod))*ksm(3,m-n-1,mod)%mod*570312504%mod)%mod<<'\n'; 
    	}
    	return 0;
    }
    

    T3 保卫王国

    思路

    不会不会,是真的不会

    哭哭

    本博文为wweiyi原创,若想转载请联系作者,qq:2844938982
  • 相关阅读:
    使用Spring MVC统一异常处理实战(转载)
    java 异常
    Java 接口和内部类
    JAVA 继承
    Linux中profile(转载)
    java 对象与类
    Java基本的程序结构设计 数组
    Python虚拟机函数机制之位置参数(四)
    Python虚拟机函数机制之参数类别(三)
    Python虚拟机函数机制之名字空间(二)
  • 原文地址:https://www.cnblogs.com/wweiyi2004/p/15560631.html
Copyright © 2020-2023  润新知