NOIP2015day2题解

大概总结下的话就是。。。。。2015那年的600分的学长,我真的是给跪了。day2的题难得真的不像话(第二题完全没有思路,第三题居然能苟到AC真的是上帝在保佑)

T1 跳石子

不那么明显的送分题,可以一眼看出是二分答案法,二分最短距离然后用队列模拟即可(唯一一道良心题)

#include<cstdio>
const int MAXN=5e4+5;
int disb[MAXN];
int L,N,M;
int ABS(int a)
{
	return a>0?a:-a;
}
bool check(int mid) 
{
	int l=M;
	int r=0;
	for(int i=1;i<=N+1;i++)
	{
		if(ABS(disb[i]-disb[r])<mid)
		{
			l--;
		}
		else
		{
			r=i;
		}
	}
	return l>=0;
}
int main()
{
	//freopen("stone.in","r",stdin);
	//freopen("stone.out","w",stdout);
	std::scanf("%d%d%d",&L,&N,&M);
	disb[0]=0;
	for(int i=1;i<=N;i++)
	{
		std::scanf("%d",&disb[i]);
	}
	disb[N+1]=L;
	int l,r;
	l=0;
	r=L+1;
	int ans;
	while(l<r)
	{
		int mid=(l+r)>>1;
		if(check(mid))
		{
			ans=mid;
			l=mid+1;
		}
		else 
		{
			r=mid;
		}
	}
	printf("%d\n",ans);
	return 0;
}
/*
25 5 2 
2
11
14
17
21
*/

T2 子串

个人认为当天最难的题,作为一道dp题目,一如既往地有令人发指的思维难度(考试的时候根本推不出来方程,事实证明字符串这种东西是真的可以灭杀一群OIer的QWQ)

这道题总共有至少三个难点

第一个是贼操蛋的状态转移方程。。。。(在超内存的基础上)不压维的话,每一个状态会由两个状态推得

我们定义一个状态数组dp[i][j][k][0/1] 0表示子串没有连在一起的时候的方案数量,1表示连在一起的方案时候的方案数量,

而正解则是两者相加

然而如果说直接开两个三维的数组的话空间会爆炸,所以说需要用滚动数组将第一个数组的访问优化(因为s1有1000而s2长度只有200)

#include<cstdio>
#include<cstring>
const int MOD=1e9+7;
char cha;
int dp[2][205][205][2],n,m,q;
char s1[1005];
char s2[205];
int main()
{
	//freopen("substring.in","r",stdin);
	//freopen("substring.out","w",stdout);
	scanf("%d%d%d\n",&n,&m,&q);
	for(int i=1;i<=n;i++)
	{
		scanf("%c",&s1[i]);
	}
	scanf("\n");
	for(int i=1;i<=m;i++)
	{
		scanf("%c",&s2[i]);
	}
	int now=1;
	dp[now][0][0][0]=1;
	for(int i=1;i<=n;i++)
	{
		now^=1;
		std::memset(dp[now],0,sizeof(dp[now]));
		for(int j=0;j<=m;j++)
		{
			for(int k=0;k<=q;k++)
			{
				if(s1[i]==s2[j+1]&&j!=m)
				{
					dp[now][j+1][k][1]=(dp[now][j+1][k][1]+dp[now^1][j][k][1])%MOD;
					dp[now][j+1][k+1][1]=((dp[now][j+1][k+1][1]+dp[now^1][j][k][0])%MOD+dp[now^1][j][k][1])%MOD;
				}
				dp[now][j][k][0]=((dp[now][j][k][0]+dp[now^1][j][k][0])%MOD+dp[now^1][j][k][1])%MOD;
			}
		}
	}
	printf("%d\n",(dp[now][m][q][0]+dp[now][m][q][1])%MOD);
	return 0;
}

第三题 二分答案+倍增

因为所有公司都是从不同地点以同一时间出发的,所以说我们完成业务的时间其实是多次询问里面长度最长的一跳链,我们要寻找的则是优化过后的最小值,所以说其实是一个查询最大值的最小值的问题,故使用二分答案二分时间来进行判断

这道题由于涉及到链查询,所以说可以用lca打标记(记录它被链经过的次数)的方法或者是树链剖分,但是事实证明链剖的时间在这道题里优于lca(更新标记的O(n)对程序的常数影响太大了),所以说lca的算法很可能只能得到95分。

还要注意先预处理LCA跟链的长度,否则会被卡常卡掉很多分

对于每一个二分出来的时间t,我们寻找超出这个t范围最多的路径,观察在所有路径都经过的链里,能否找到一跳边,使其开了虫洞以后能够刚好满足把这个t容纳下去,如果能我们就把t再往小的二分,不能就把t分得更大

写仔细一点是不会有错的

#include<cstdio>
#include<cstring>
#include<algorithm>
const int MAXN=3e5+5;
const int P=20+1;
struct Edge
{
	int nxt;
	int to;
	int w;
}edge[MAXN<<1];
int head[MAXN];
int num;
void add(int from,int to,int w)
{
	edge[++num].nxt=head[from];
	edge[num].to=to;
	edge[num].w=w;
	head[from]=num;
}
int anc[MAXN][P+1];
int dep[MAXN];
int wf[MAXN];
int dist[MAXN];
int idc=0;
int seq[MAXN];
int uu[MAXN];
int vv[MAXN];
int dgr[MAXN];
int cost[MAXN];
int LCA[MAXN];
void dfs(int u,int f)
{
	anc[u][0]=f;
	for(int p=1;p<=P;p++)
	{
		anc[u][p]=anc[anc[u][p-1]][p-1];
	}
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].to;
		if(v==f)
		{
			continue;
		}
		dep[v]=dep[u]+1;
		dist[v]=dist[u]+edge[i].w;
		wf[v]=edge[i].w;
		dfs(v,u);
	}
	seq[++idc]=u;
}
int lca(int u,int v)
{
	if(dep[u]<dep[v])
	{
		std::swap(u,v);
	}
	int t=dep[u]-dep[v];
	for(int p=0;t;t>>=1,p++)
	{
		if(t&1)
		{
			u=anc[u][p];
		}
	}
	if(u==v)
	{
		return u;
	}
	for(int p=P;p>=0;p--)
	{
		if(anc[u][p]!=anc[v][p])
		{
			u=anc[u][p];
			v=anc[v][p];
		}
	}
	return anc[u][0];
}
int n,m;
void solve()
{
	for(int i=1;i<=m;i++)
	{
		int u=uu[i];
		int v=vv[i];
		int ancc=lca(u,v);
		cost[i]=dist[u]+dist[v]-2*dist[ancc];
		LCA[i]=ancc;
	}
}
bool check(int t) 
{
    int judge=0,rest=0;
    memset(dgr,0,sizeof(dgr));
    for (int i=1;i<=m;i++) 
	{
        if(cost[i]<=t) continue; 
        judge++;
        int u=uu[i];
        int v=vv[i];
        int ancc=LCA[i];
        dgr[u]++;
	dgr[v]++;
	dgr[ancc]-=2;
        rest=std::max(rest,cost[i]-t);
    }
    for (int i=1;i<=n;i++)
	{
        int u=seq[i];
        dgr[anc[u][0]]+=dgr[u];
    }
    int hole_tim=0;
    for (int i=1;i<=n;i++)
    {
    	if (dgr[i]==judge) 
    	{
    		hole_tim=std::max(hole_tim,wf[i]);
    	}
    }
    return rest<=hole_tim;
}
int main()
{
	freopen("transport.in","r",stdin);
	freopen("transport.out","w",stdout);
	std::scanf("%d%d",&n,&m);
	for(int i=1;i<=n-1;i++)
	{
		int u,v,w;
		std::scanf("%d%d%d",&u,&v,&w);
		add(u,v,w);
		add(v,u,w);
	}
	for(int i=1;i<=m;i++)
	{
		int u,v;
		std::scanf("%d%d",&u,&v);
		uu[i]=u;
		vv[i]=v;
	}
	dep[1]=1;
	dfs(1,1);
	solve();
	int l=0;
	int r=3e8;
	int ans;
	while(l<r)
	{
		int mid=(l+r)>>1;
		if(check(mid))
		{
			ans=mid;
			r=mid;
		}
		else 
		{
			l=mid+1;
		}
	}
	std::printf("%d\n",ans);
	return 0;
}
/*
6 3
1 2 3
1 6 4
3 1 7
4 3 6
3 5 5
3 6
2 5
4 5
*/

猜你喜欢

转载自blog.csdn.net/Amuseir/article/details/80207466
今日推荐