暑期集训第九天(6-30)题解及总结

Posted li-jia-hao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了暑期集训第九天(6-30)题解及总结相关的知识,希望对你有一定的参考价值。

小概括:

今天的考试中老师可能对我们过于高估了...四道之中出了两道紫题,于是这次考试之中的分数基本都很低(除了AK的gyz大佬),DZN今天终于超过了lc(排除提示的问题)今天晚上周围集体都在打树剖,可能认为老师明天要考???

T1:浇水

技术图片

 

 

 听说这道题要用贪心?线段树?对不起,最短路解决一切问题,我们考虑题目中要求覆盖整个花园,其实可以转化为覆盖上边界(下边界同理),我们就可以处理出每个喷子的可以喷到的上边界的范围,之后就是把他们拼起来即可,之后就和昨天的最后一道题一样了,注意喷子够不到上边界的要判掉,不然就算成负权的加入图中了

(链接跳转:昨日博客:https://www.cnblogs.com/li-jia-hao/p/13210357.html

老姚贪心写法https://www.cnblogs.com/hbhszxyb/p/13212743.html;)

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
struct Node{
	int next,to,dis;
}edge[N];
int Head[N],tot;
void Add(int x,int y,int z){
	edge[++tot].to=y;
	edge[tot].dis=z;
	edge[tot].next=Head[x];
	Head[x]=tot;
}
struct Edge{
	int id,dis;
	Edge(int x,int y){
		id=x;dis=y;
	}
	bool operator < (const Edge &a)const{
		return a.dis<dis;
	}
};
priority_queue<Edge>q;
int dis[N],vis[N];
void dijkstra(int x){
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	dis[x]=0;q.push(Edge(x,0));
	while(!q.empty()){
		int u=q.top().id;q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=Head[u];i;i=edge[i].next){
			int v=edge[i].to;
			if(dis[v]>dis[u]+edge[i].dis){
				dis[v]=dis[u]+edge[i].dis;
				q.push(Edge(v,dis[v]));
			}
		}
	}
}
int main(){
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	int k,n,m;
	scanf("%d%d%d", &k, &n, &m);
	for(int i = 1; i <= k; ++ i){
		int x,y,l,r;
		scanf("%d%d",&x,&y);
		x++;
		if(y < m/2) continue;
		l=x-(int)sqrt(y*y-m*m/4);
		r=x+(int)sqrt(y*y-m*m/4);
		//printf("%d %d %d
",i,l,r);
		l=max(l,1);
		r=min(r,n+1);
		Add(l,r+1,1);
	}
	for(int i=1;i<=n+2;++i)
		Add(i+1,i,0);
	dijkstra(1);
	if(dis[n]==0x3f3f3f3f) printf("-1
");
	else printf("%d
",dis[n+1]);
	return 0;
}

 T2:免费馅饼

技术图片

 

 

 这道题后来被削弱了,老师看我们对字典序不怎么理解,就把第二部分的路径输入给去了(然鹅我自信满满的dp只得了30pts),如果不考虑路径的话这道题还是挺简单的,就是一个线性dp,我们预先处理出i时在j可以得到的分数,dp[i][j]表示时间为i时在j的答案,于是我们从答案可能转移来的方向进行转移求最大值即可,我30pts是因为忘了原地踏步的情况,记得初始化及特判高度为1的情况,

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int score[1500][150];
int dp[1500][150]; //i秒的时候在j时的答案;
int n,m,t_max,sum;
int main(){
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	scanf("%d%d",&n,&m);
	int a,b,c,d;
	while(scanf("%d%d%d%d",&a,&b,&c,&d)==4){
		if(m==1){
			score[a][b]+=d;sum+=d;
			t_max=max(a,t_max);
			continue;
		}
		if((m-1)%c!=0) continue;
		int t=((m-1)/c); 
		score[a+t][b]+=d;
		t_max=max(a+t,t_max);
	}
	memset(dp,128,sizeof(dp));
	dp[0][(n+1)/2]=0;
	for(int i=1;i<=t_max;++i)
		for(int j=1;j<=n;++j){
			if(dp[i][j]<dp[i-1][j]+score[i][j]){
				dp[i][j]=dp[i-1][j]+score[i][j];
			}
			if(j>1)
				if(dp[i][j]<dp[i-1][j-1]+score[i][j]){
					dp[i][j]=dp[i-1][j-1]+score[i][j];
				}
			if(j<=n-2)
				if(dp[i][j]<dp[i-1][j+2]+score[i][j]){
					dp[i][j]=dp[i-1][j+2]+score[i][j];
				}
			if(j<=n-1)
				if(dp[i][j]<dp[i-1][j+1]+score[i][j]){
					dp[i][j]=dp[i-1][j+1]+score[i][j];
				}
			if(j>2)
				if(dp[i][j]<dp[i-1][j-2]+score[i][j]){
					dp[i][j]=dp[i-1][j-2]+score[i][j];
				}
				
		}
	int ans=0;
	for(int i=1;i<=n;++i){
		ans=max(ans,dp[t_max][i]);
	}
	if(n==1&&m==1){
		printf("%d
",sum);
		return 0;
	}
	printf("%d
",ans);
	return 0;
}

 T3:压缩

技术图片

 

 

 这是考试之中我唯一一道A掉的dp题,我们用dp[i][j][k]表示从i到j的情况,k=1表示有M,否则表示没有M,那么我们存在以下几个转移:

1. dp[i][j][0]=min(dp[i][j][0],dp[i][mid][0]+1)(当且仅当字符串对称)

2. dp[i][j][0]=min(dp[i][j][0],dp[i][k][0]+j-k)

3. dp[i][j][1]=min(dp[i][j][1],min(dp[i][k][1],dp[i][k][0])+min(dp[k+1][j][1],dp[k+1][j][0])+1)(最后加的一是缺失的M);

之后我们闭着眼进行转移就行了.

#include<bits/stdc++.h>
#define debug printf("-debug-
")
using namespace std;
const int N=1e6+10;
char a[N];
int dp[250][250][2];
bool Judge(int l,int r){
	if((r-l+1)%2!=0) return 0;
	int mid=l+(r-l+1)/2;
	while(mid<=r){
		if(a[l]!=a[mid]) return 0;
		l++;mid++;
	}
	//debug;
	return 1;
}
int main(){
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	scanf(" %s",a+1);
	int len=strlen(a+1);
	for(int i=1;i<=len;++i)
		for(int j=i;j<=len;++j)
			dp[i][j][0]=dp[i][j][1]=j-i+1;
	//debug;
	for(int d=2;d<=len;++d)
		for(int i=1,j;(j=i+d-1)<=len;++i){
			//debug;
			int mid=i+(j-i+1)/2-1;
			if(Judge(i,j)) dp[i][j][0]=min(dp[i][j][0],dp[i][mid][0]+1);
			for(int k=i;k<=j;++k)
				dp[i][j][0]=min(dp[i][j][0],dp[i][k][0]+j-k);
			for(int k=i;k<j;++k)
				dp[i][j][1]=min(dp[i][j][1],min(dp[i][k][1],dp[i][k][0])+min(dp[k+1][j][1],dp[k+1][j][0])+1);
		}
	//debug;
	printf("%d
",min(dp[1][len][1],dp[1][len][0]));
	return 0;
}

 T4:枪战Maf

技术图片

 

 我们老师介绍这道题属于那种看起来就有思路,越看越有思路,但是越写越绝望的题目,因为考场上没时间细想,不怎么以为然,知道我开始详细的打这道题....

这道题一看就能想到tarjan缩点,但是如果这样的话你就掉进出题人的陷阱了,这道题的思路我感觉...自己想出来的非神即犇.

考虑如果单独的一个环,最大的情况就是一个人打完后立马被另一个人打死,这样死的人数就是环的大小减一,最大就是环的大小除以2

如果是链那?最多人数当然是除了出度为0的点的人以外全部死掉,最少人数就是大小处以二.我们可以对每个人进行处理,入度为0的点必不死,可死可不死的人可以打上标记,稍后处理,之后对环单独处理,这里的思路可能有些不好想,推一下老师的博客:https://www.cnblogs.com/hbhszxyb/p/13212782.html

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int die[N],undie[N];
int q[N],Max,Min,ru[N],to[N];
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d",&to[i]);
		ru[to[i]]++;
	}
	for(int i=1;i<=n;++i)
		if(ru[i]==0){
			Min++;
			q[++Max]=i;
		}
	int head=0;
	while(head<=Max){
		int top=q[head];head++;
		if(die[to[top]]) continue;
		die[to[top]]=1;
		int live=to[to[top]];
		ru[live]--;
		undie[live]=1;
		if(ru[live]==0) q[++Max]=live;
	}
	for(int i=1;i<=n;++i)
        if(ru[i]&&!die[i]){
            int len=0,flag=0;
            for(int j=i;!die[j];j=to[j]){
                len++;
                flag|=undie[j];
                die[j]=1;
            }
            if(!flag && len>1) Min++;
            Max+=len/2;
    } 
    printf("%d %d",n-Max,n-Min);
    return 0;
}

 总结:

今天的考试我感觉还是挺难的,虽然出现了集训以来的第一个AK(G大佬),但是对于我们这些小萌新来说就是虐杀呀......平时只练蓝题还是不够,平时还是要穿插一些有难度的题目呀,高三学长们马上要高考了,在现在的机房也待不了几天了,希望明天可以取得一个好成绩吧,.

以上是关于暑期集训第九天(6-30)题解及总结的主要内容,如果未能解决你的问题,请参考以下文章

暑期集训第六天(6-27)自由复习的题目练习及总结

15/7/2017 暑期第一次集训小总结

题解报告(CDUT暑期集训——第三场)

SDU暑期集训排位题解

暑期集训第一场欧拉回路 | 思维 | 数论构造 | 容斥原理 | 线段树 | 归并排序

蓝桥集训(附加面试题)第九天