zoukankan      html  css  js  c++  java
  • NOIP2015题解

    NOIP2015题解

    Day1

    神奇的幻方 magic

    模拟裸题。我在NOIP切掉的第一道题

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int n,a[50][50],x,y;
    int main()
    {
    	scanf("%d",&n);
    	x=1;y=(n+1)>>1;
    	for(int i=1;i<=n*n;++i)
    	{
    		a[x][y]=i;x-=1;y+=1;
    		if(x==0)
    		{
    			if(y>n)x=2,y=n;
    			else x=n;
    		}
    		else if(y>n)y=1;
    		else if(a[x][y])x+=2,y-=1;
    		
    	}
    	for(int i=1;i<=n;++i,puts(""))
    		for(int j=1;j<=n;++j)
    			printf("%d ",a[i][j]);
    	return 0;
    }
    

    信息传递 message

    不难发现就是让你求基环森林的最小环,直接(Tarjan)了。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define MAX 200200
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    struct Line{int v,next;}e[MAX];
    int h[MAX],cnt=1;
    inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
    int n,ans=1e9;
    int dfn[MAX],low[MAX],tim,S[MAX],top;
    bool ins[MAX];
    void Tarjan(int u)
    {
    	dfn[u]=low[u]=++tim;S[++top]=u;ins[u]=true;
    	for(int i=h[u];i;i=e[i].next)
    	{
    		int v=e[i].v;
    		if(!dfn[v])Tarjan(v),low[u]=min(low[u],low[v]);
    		else if(ins[v])low[u]=min(low[u],dfn[v]);
    	}
    	if(low[u]==dfn[u])
    	{
    		int sz=0,v;
    		do{v=S[top--];ins[v]=false;++sz;}while(u!=v);
    		if(sz>1)ans=min(ans,sz);
    	}
    }
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;++i)Add(i,read());
    	for(int i=1;i<=n;++i)if(!dfn[i])Tarjan(i);
    	printf("%d
    ",ans);
    	return 0;
    }
    

    斗地主 landlords

    很妙的题。而且我的这里写的是可以被(Hack)的。

    发现出牌有两种关系,一种与点数无关,一种与点数相关。那么当对于点数无关的时候,我们贪心求一次解,点数相关的时候我们爆搜顺子。

    这样子可以过这题,但是有问题。因为贪心是假的,假的原因是你要考虑一些牌是可以拆开打的,比如说你有三个炸弹,理论上说是出(3)次。事实上你可以把其中一个拆成两对,这样子(2)次就打出去了。所以把贪心换成(dp)就没有问题了。然而数据随机,贪心基本就行了。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define ll long long
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int n,ans;
    int a[20],b[20];
    void dfs(int st)
    {
    	for(int i=0;i<=14;++i)b[a[i]]+=1;
    	for(int i=1;i<=14;++i)
    		if(a[i]==4)
    		{
    			if(b[1]>=2)b[1]-=2;
    			else if(b[2]>=2)b[2]-=2;
    			else if(b[2])b[2]-=1;
    		}
    	for(int i=1;i<=14;++i)
    		if(a[i]==3)
    		{
    			if(b[1])b[1]-=1;
    			else if(b[2])b[2]-=1;
    		}
    	ans=min(ans,st+b[1]+b[2]+b[3]+b[4]);
    	b[0]=b[1]=b[2]=b[3]=b[4]=0;
    	for(int i=3;i<=10;++i)
    		for(int j=i;j<=15;++j)
    		{
    			if(!a[j]){for(int k=i;k<j;++k)a[k]+=1;break;}
    			a[j]-=1;if(j-i+1>=5)dfs(st+1);
    		}
    	for(int i=3;i<=12;++i)
    		for(int j=i;j<=15;++j)
    		{
    			if(a[j]<2){for(int k=i;k<j;++k)a[k]+=2;break;}
    			a[j]-=2;if(j-i+1>=3)dfs(st+1);
    		}
    	for(int i=3;i<=13;++i)
    		for(int j=i;j<=15;++j)
    		{
    			if(a[j]<3){for(int k=i;k<j;++k)a[k]+=3;break;}
    			a[j]-=3;if(j-i+1>=2)dfs(st+1);
    		}
    }
    int main()
    {
    	freopen("landlords.in","r",stdin);
    	freopen("landlords.out","w",stdout);
    	int T=read();n=read();
    	while(T--)
    	{
    		for(int i=0;i<=14;++i)a[i]=0;
    		for(int i=1;i<=n;++i)a[read()]+=1,read();
    		a[14]=a[1];a[1]=0;ans=n;dfs(0);
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    Day2

    跳石头 stone

    又想起我当年连这种题都不会做(雾

    二分答案,直接扫一遍就好了。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define MAX 50050
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int L,n,m,S[MAX],top,d[MAX];
    bool check(int s)
    {
    	S[top=0]=0;int mov=0;
    	for(int i=1;i<=n;++i)
    	{
    		if(d[i]<s){++mov;continue;}
    		if(top&&d[i]-S[top]<s){++mov;continue;}
    		S[++top]=d[i];
    	}
    	while(top&&L-S[top]<s)--top,++mov;
    	return mov<=m;
    }
    int main()
    {
    	L=read();n=read();m=read();
    	for(int i=1;i<=n;++i)d[i]=read();
    	if(!n){printf("%d
    ",L);return 0;}
    	int l=0,r=1e9,ret=0;
    	while(l<=r)
    	{
    		int mid=(l+r)>>1;
    		if(check(mid))l=mid+1,ret=mid;
    		else r=mid-1;
    	}
    	printf("%d
    ",ret);return 0;
    }
    

    子串 substring

    (dp)题。

    (f[i][j][k][0/1])表示当前考虑(A)串的第(i)位,(B)串匹配了第(j)个字符,当前已经分了(k)段,当前(i)位置的字符是否在最后一段中的方案数。

    转移不难,看看代码就知道了。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    #define MOD 1000000007
    void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
    char A[1010],B[220];
    int f[2][220][220][2];
    int n,m,K;
    int main()
    {
    	scanf("%d%d%d",&n,&m,&K);
    	scanf("%s",A+1);scanf("%s",B+1);
    	f[0][0][0][0]=1;
    	for(int i=1,nw=1,pw=0;i<=n;++i,nw^=1,pw^=1)
    	{
    		memset(f[nw],0,sizeof(f[nw]));
    		for(int j=0;j<=m;++j)
    			for(int k=0;k<=K;++k)
    			{
    				add(f[nw][j][k][0],f[pw][j][k][0]);
    				add(f[nw][j][k][0],f[pw][j][k][1]);
    				if(A[i]==B[j])
    				{
    					add(f[nw][j][k][1],f[pw][j-1][k][1]);
    					if(k)
    					{
    						add(f[nw][j][k][1],f[pw][j-1][k-1][0]);
    						add(f[nw][j][k][1],f[pw][j-1][k-1][1]);
    					}
    				}
    			}
    	}	
    	printf("%d
    ",(f[n&1][m][K][0]+f[n&1][m][K][1])%MOD);
    	return 0;
    }
    

    运输计划 transport

    现在看是真的简单题。。。

    二分答案,显然时间本来就小于二分值的就不用管,只考虑时间大于二分值的。那么显然它要改变路径上的一条边时间才会变小,那么树上差分找到可以改变所有计划的一条最长边。然后用最大的时间减去最长边边长和二分值比较判定就做完了。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    #define MAX 300300
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    struct Line{int v,next,w;}e[MAX<<1];
    int h[MAX],cnt=1,df[MAX];
    inline void Add(int u,int v,int w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
    int n,m,mx;
    namespace LCA
    {
    	int dep[MAX],fa[MAX],top[MAX],size[MAX],hson[MAX],dis[MAX];
    	void dfs1(int u,int ff)
    	{
    		size[u]=1;fa[u]=ff;dep[u]=dep[ff]+1;
    		for(int i=h[u];i;i=e[i].next)
    		{
    			int v=e[i].v;if(v==ff)continue;
    			dis[v]=dis[u]+e[i].w;df[v]=e[i].w;
    			dfs1(v,u);size[u]+=size[v];
    			if(size[hson[u]]<size[v])hson[u]=v;
    		}
    	}
    	void dfs2(int u,int tp)
    	{
    		top[u]=tp;if(hson[u])dfs2(hson[u],tp);
    		for(int i=h[u];i;i=e[i].next)
    			if(e[i].v!=fa[u]&&e[i].v!=hson[u])dfs2(e[i].v,e[i].v);
    	}
    	int LCA(int u,int v)
    	{
    		while(top[u]^top[v])dep[top[u]]<dep[top[v]]?v=fa[top[v]]:u=fa[top[u]];
    		return dep[u]<dep[v]?u:v;
    	}
    	int Dis(int u,int v){return dis[u]+dis[v]-2*dis[LCA(u,v)];}
    }
    struct Plan{int u,v,lca,d;}p[MAX];
    int a[MAX];
    void dfs(int u,int ff)
    {
    	for(int i=h[u];i;i=e[i].next)
    		if(e[i].v!=ff)dfs(e[i].v,u),a[u]+=a[e[i].v];
    }
    bool check(int t)
    {
    	memset(a,0,sizeof(a));
    	int tot=0;
    	for(int i=1;i<=m;++i)
    		if(p[i].d>t)
    			++tot,a[p[i].u]+=1,a[p[i].v]+=1,a[p[i].lca]-=2;
    	dfs(1,0);
    	int mxt=0;
    	for(int i=2;i<=n;++i)
    		if(a[i]==tot)
    			mxt=max(mxt,df[i]);
    	return mx-mxt<=t;		
    }
    int main()
    {
    	n=read();m=read();
    	for(int i=1;i<n;++i)
    	{
    		int u=read(),v=read(),w=read();
    		Add(u,v,w);Add(v,u,w);
    	}
    	LCA::dfs1(1,0);LCA::dfs2(1,1);
    	for(int i=1;i<=m;++i)
    	{
    		int u=read(),v=read();
    		p[i]=(Plan){u,v,LCA::LCA(u,v),LCA::Dis(u,v)};
    		mx=max(mx,p[i].d);
    	}
    	int l=0,r=mx,ret=mx;
    	while(l<=r)
    	{
    		int mid=(l+r)>>1;
    		if(check(mid))r=mid-1,ret=mid;
    		else l=mid+1;
    	}
    	printf("%d
    ",ret);
    	return 0;
    }
    
  • 相关阅读:
    CSP-S2020总结
    题解-P6687 论如何玩转 Excel 表格
    题解-UVA12995 【Farey Sequence】
    题解-P4159 [SCOI2009] 【迷路】
    题解-SP2916【GSS5
    102. 二叉树的层序遍历
    力扣 160 相交链表 快慢指针 双指针
    3. 无重复字符的最长子串 滑动窗口
    最大连续1的个数 III
    B树和B+树
  • 原文地址:https://www.cnblogs.com/cjyyb/p/9931796.html
Copyright © 2011-2022 走看看