暑期集训第九天(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\n",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\n");
else printf("%d\n",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\n",sum);
return 0;
}
printf("%d\n",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-\n")
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\n",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大佬),但是对于我们这些小萌新来说就是虐杀呀......平时只练蓝题还是不够,平时还是要穿插一些有难度的题目呀,高三学长们马上要高考了,在现在的机房也待不了几天了,希望明天可以取得一个好成绩吧,.

浙公网安备 33010602011771号