2022高考集训3

2022高考集训3

6月7日

前言:

     我们先写第三天的,因为第二天的我现在还有三道题没改完,笑死了,众生平等日。好了,姑且不去说它。6日晚上因为改题改不出来就吃巧克力,结果一不留神就吃了7,8块那种甜到发腻的白巧,结果晚上那个嗓子就崩溃了,我咽个唾沫都有一种灼烧感,一晚上就没睡好。早上起来都说不了话了。7号考试上午我就一直灌水,灌啊灌,然后接着灌,接着灌完继续灌。早上吃饭回来新放的一桶水,他们基本都没有怎么喝,让我自己干了半桶,当然效果是显著的,我现在嗓子已经不是那么疼了,但是感冒还没好且愈发严重了。。。。。

    又因为对巧克力有恐惧了,加上ljx说byw特别喜欢吃巧克力,所以就中午就送了一盒给ljx,让他送给byw,我还特地给他那盒心形的没拆封的,这本来打算送myh的。byw也让ljx带坏了,两人见了我,都来一句王队。我就莫名有种感觉,byw跟着ljx喊王队的时候两个人真的很有夫妻相啊,我倒是挺祝愿他俩一直走下去的。扯远了,我们回去

    因为一直灌水+上厕所,所以题也没有怎么好好写了,可能状态也确实是不好,最后还因为废弃的数组没删结果因为它re了40分

贴下成绩

rk8,可以说是非常不满意的,加上re的四十是rk3,但还是和rk1有差距,rk1AK了,又因为rk1是某人(好吧就是WinersRain),所以我不太想接受这个现实,就没截它,又因为第三天这次考得几道题洛谷上都有,所以就直接截图我自己博客就不加密了

T1

Watching Fireworks is Fun

这个题是个单调队列优化DP,首先可以看出是个DP,因为从不同的位置跑到某个位置获得的幸福度都是不一样的,肯定是要一层层的转移出来,他可不是越近越好,他可能这个近了,下个再跑就困难了,所以不能每次贪心的走最近,一定要dp去循环(这是351238提出的疑惑)。

dp[i][j]第i个烟花释放时在j位置可以获得的最大幸福,i只和i-1,可压一维

 那么转移方程就是dp[i][j]=max(dp[i-1][j+-t*d])+幸福值,很明显,+-d*t的值是不变的,所以我们没有必要去开个k再循环,直接对于同一个i的不同j,单调队列维护一个max(dp[i-1][k])即可

复杂度是O(n*m)可过,我考场正反走两遍,j-d*t到j和j+d*t到j,这样滑动,我后来又想了想,其实没必要,绝对值就解决了,但后来也懒得改了,WinersRain好像就只扫了一遍,这个题一开始开了个wor数组以t为下标,是最开始得一种思路,后来改了也忘了删,结果t直接1e9那个数组re了卡我40分

查看代码

//Watching Fireworks is Fun
//啥也别想了,DP,而且单调优化
//这一秒转移过来不同的距离,单调优化
//这两周光做这个了,都快透了 
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
const int mx=1500000+1000;
int n,m;
ll d;
ll dp[5][mx];//dp[i][j]第i秒不对 是 (第i个烟花释放时)在j位置可以获得的最大幸福,i只和i-1,压一维 
struct Node{
	ll a;
	ll b;
	ll t;
}fw[mx];
int wor[mx];
int wor1[mx];
bool vis[mx];
ll sum[mx];//单调优化老数组了 
int q[mx];
int l=1,r=0; 
ll ans=9999999999999;//上次我可吃大亏了 
//总不能真万亿幸福吧直接升天? 
void Solve(){
	scanf("%d%d%lld",&n,&m,&d);
	int T=0;
	memset(dp,0x3f,sizeof(dp));
	for(int i=1;i<=m;++i){
		ll a,b,t;
		scanf("%lld%lld%lld",&a,&b,&t);
		fw[i].a=a;
		fw[i].b=b;
		fw[i].t=t;
	//	wor[t]=b;
	//	wor1[t]=a;
//		T=max(T,t);
		sum[i]=sum[i-1]+b; 
	//	printf("%lld\n",sum[i]);
	}
	for(int i=1;i<=n;++i){
		dp[1][i]=abs(fw[1].a-i);
	}
	int pd=0;
	int pp=1; 
	for(int i=2;i<=m;++i){
		if(i%2==0){
			pd=2;
			pp=1;
		}
		else {
			pp=2;
			pd=1;//这个操作属实不符合我的水平
		}
		memset(dp[pd],0x3f,sizeof(dp[pd]));//这里千万被忘啊啊啊啊啊啊啊 
	    ll x=(fw[i].t-fw[i-1].t)*d;//用烟花轮,可跨越的距离 
		l=1,r=0;
		//每次都必须轮j,m*n大概可以 
		for(int j=1;j<=n;++j){
			while(l<=r && j-q[l]>x)l++;//先距离
			while(l<=r && dp[pp][j]<=dp[pp][q[r]])r--;
			q[++r]=j;
			dp[pd][j]=min(dp[pd][j],dp[pp][q[l]]+abs(fw[i].a-j));
		} 
		l=1,r=0;
		//这里有最大跳跃距离那个题的思想
		//他可以从前从后两次来,看着一样,其实不太一样,大概不太一样
		//总不能是我dp定义丑了吧,不能吧 
		for(int j=n;j>=1;--j){//其实据我推算,倒序的话,应该直接一维就行
		//但是怎么说,怕写挂不是,还是开个二维
		    while(l<=r && q[l]-j>x)l++;
		    while(l<=r && dp[pp][q[r]]>=dp[pp][j])r--;
		    q[++r]=j;
		    dp[pd][j]=min(dp[pd][j],dp[pp][q[l]]+abs(fw[i].a-j));
			
		} 
	}
	int en=0;
	if(m%2==0)en=2;
	//这里真的很丑啊,一定学二进制 
	else en=1;
	for(int i=1;i<=n;++i){
		ans=min(ans,dp[en][i]);
	} 
	ll an=sum[m]-ans;
//	printf("%lld ans=%lld\n",sum[m],ans);
	printf("%lld\n",an);
	//我样例直接1A,别这样,我害怕 
/*	for(int i=1;i<=m;++i){
		for(int j=1;j<=n;++j){
			//那这个d就要乘上时间t了 
			//wor是不变的,变的是k
			//不对啊
			//md这是第一题的难度嘛
			//奶牛那个题,正难则反 
			//我不去维护答案,我去维护,奶牛要工作
			//所以维护工作损耗,我要幸福,所以维护幸福损耗
			//就直接维护ai-x呗,同奶牛一样,最后的总要求b是一样的
			dp[i][j]=max(dp[i-1][j+ -d])
			//+ -d 在j-d <= k <=j+d 里维护一个k 
			//它+d其实应该再倒着来一遍,不然不太好写 
		} 
	} */
/*	for(int i=1;i<=T;++i){
		for(int j=1;j<=n;++j){ //t*n还是离谱,就是得拿烟花轮 
			for(int k=j-d;k<=j+d;++k){//拿时间跳?拿烟花跳? 
				dp[i][j]=max(dp[i][j],dp[i-1][k]+wor[i-1]-abs(wor1[i-1]));//就这????????? 
			}
		}
	}*/
	
}
int main(){
//	freopen("data.in","r",stdin);
//	freopen("out.out","w",stdout);
	freopen("fire.in","r",stdin);
	freopen("fire.out","w",stdout);
	Solve();
	return 0;
}

 我再贴下WinersRain的

查看代码

#include<cstdio>
#include<cstring>
#include<string>
#define WR WinterRain
#define int long long
#define Dream signed
using namespace std;
const int WR=1001000,INF=1152921504606846976;//皮一下2^60
struct FireWork{
    int a,b,t;
}fire[WR];
int n,m,d;
int ans;
int tmp[WR],dp[WR];
int que[WR];
int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=(s<<3)+(s<<1)+ch-48;
        ch=getchar();
    }
    return s*w;
}
Dream main(){//想起Dream那次烟花火箭送Muffin Team上天
    freopen("fire.in","r",stdin);
    freopen("fire.out","w",stdout);
    n=read(),m=read(),d=read();
    for(int i=1;i<=m;i++){
        fire[i].a=read(),fire[i].b=read(),fire[i].t=read();
    }
    for(int i=1;i<=m;i++){
        memcpy(tmp,dp,sizeof(dp));//本来我想开循环赋值的
        //但是想到上次T2爆零
        //爷即使TLE也不用循环了
        if(fire[i].t==fire[1].t){//其实看着后面的单调队列这个有点多余了
            for(int j=1;j<=n;j++){//但是以防万一加上吧
                dp[j]=tmp[j]+fire[i].b-abs(fire[i].a-j);
            }
            continue;
        }
        //这么一看普通动规还过不了n*m^2
        //希望我的复杂度算的没错(
        //得单调队列优化???别吧
        int l=1,r=0;
        int k=1;//k表示在哪个位置
        int dis=(fire[i].t-fire[i-1].t)*d;//dis表示在两次烟花之间可以跑多远
        for(int j=1;j<=n;j++){
            while(k<=min(dis+j,n)){//总不能跑出去吧
                while(l<=r&&tmp[k]>=tmp[que[r]]) r--;//单调队列存储的是地点
                que[++r]=k;
                k++;
            }//我害怕它会被卡成n^2......
            while(l<=r&&j-dis>que[l]) l++;//必须能从j点跑到l
            dp[j]=tmp[que[l]]+fire[i].b-abs(fire[i].a-j);
        }
    }
    ans=-INF;
    for(int i=1;i<=n;i++) ans=max(ans,dp[i]);
    printf("%lld",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

T2

 Perform巡回演出

我当时考场做嗨了,应该是2,4简单,1,3难,A了2,4的不少,A 1,3的没几个,我是除了rk1之外唯一一个把1,3A了的人,主要T3有意思,我就做了两个小时,回来也没细细分析就直接去做T1了,没想到T2,T4这么简单

这个没啥好所的,一个普通DP,显然出的东西,就是题面挺复杂,我考后改的时候因为dp[][]数组的i,j大小和定义反了,就一直改不过去,气死了

查看代码


//Perform巡回演出
//“艺术家”! 
//这没有办法跑最短路,所以只能是个DP
//而且n<=10,但状压也不好写啊,那就应该是个普通
//dp[i][j]= 第i天在城市j的最小值? 
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
const int mx=4000;
struct Node{
	int d;
	int moy[40];
	//20*20*40=400*40=16000 ok 
}fly[50][2000]; 
ll dp[1010][20];//也是,这一天只和上一天有关,压一维 ,time也不大,没有必要压
void clear(){
	memset(dp,0,sizeof(dp));
	//fly不清空大概没有问题吧,我说应该 
} 
void Solve(){
	int cnt=0;
   // printf("kkkkkkkkkk\n");
	while(1){
		cnt++;
        
		int n,k;
		scanf("%d%d",&n,&k);
		if(n==0 && k==0)break;
//		auto nn=n*(n-1);
		for(auto i=1;i<=n;++i){
            int pd=0;
			for(auto j=1;j<=n-1;++j){
			//	printf("j=%d\n",j);
				auto jj=j;
                if(pd==1)jj++;
				if(i==jj){
                    pd=1;
                    jj++;
                }
				int d;
				scanf("%d",&d);
			//	printf("kk %d\n",d);
				fly[i][jj].d=d;
            //    printf("f[%d][%d]=%d\n",i,jj,fly[i][jj].d);
				for(auto k=1;k<=d;++k){
					int x;
					scanf("%d",&x);
		//			printf("kkk %d\n",x);      
					fly[i][jj].moy[k]=x;

				} 
			}
	//		printf("i=%d\n",i);
		}
       
        //这个题怎么这么简单啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊
        //但凡我没有做T3做两个半小时
        //这个题这不秒切?
        //后悔了,应该先做T2,再做T4,再T1,T3
        //而不是切完T3,T1之后 连分析T2的时间都没有
        memset(dp,0x3f,sizeof(dp));
     //   dp[1][1]=0;
        for(int i=2;i<=n;++i){
            if(fly[1][i].moy[1]==0)continue;
            dp[1][i]=fly[1][i].moy[1];
        }
        
        for(auto i=2;i<=k;++i){
      //      printf("kkk i=%d\n",i);
            for(auto j=1;j<=n;++j){
          //      printf("kk j=%d\n",j);
                for(auto k=1;k<=n;++k){
            //        printf("ooo k=%d\n",k);
                    if(k==j)continue;
           //         if(fly[k][j])
            //        printf("pppppppppppppppppp\n");
            //        printf("i=%d fly[k]=%d\n",i,fly[k][j].d);
                    int t=i%fly[k][j].d;
            //        printf("kkkkkkkkkkkkkkk\n");
                    if(t==0)t=fly[k][j].d;
                    if(fly[k][j].moy[t]==0)continue;
                    dp[i][j]=min(dp[i][j],dp[i-1][k]+fly[k][j].moy[t]);
                //    printf("dp[%d][%d]=%lld\n",i,j,dp[i][j]);
                }
            }
        }
      //  printf("kkkkkkkkkkk\n");
        ll anss=99999999999;
        auto ans=anss;//auto 不太能通过具体的数值推导到ll,它只能是l int
       /* for(int i=1;i<=n;++i){
            ans=min(dp[k][i],ans);
        }*/
        ans=dp[k][n];
        if(ans>=99999999){
        	printf("0\n");
		}
        else printf("%lld\n",ans);
	}
}

int main(){	
    freopen("perform.in","r",stdin);
	freopen("perform.out","w",stdout);
	Solve();
	return 0;
}

T3

枪战Maf

这个题我一看就兴趣来了,然后瞎搞了两个多小时搞出来了。下午讲题的时候我特地跟WinersRain说我想讲这个题,干饭了下午再写。首先,依据瞄准关系建个图,没有入度的点一定不会死,那么同样,他指向的人一定会死。从没有入度的点开始,求最大他最后开枪求最小他第一个开枪。我们又可得,如果是一个环,最少要死一半(间隔开枪,且这个枪一定要响),最多死得只剩下一个。这就是模拟的思路。把模拟进行优化,分析发现,图中不会出现一条链的情况,因为每个人必定指向一个人(哪怕是指自己),图中要么是环,要么是链加环,或者这两者结合,但基本图就是这两种。

一个环:n/2和1

一个链+环:环上的点可以全死 链上的点依次记录即可。

我们完全可以从入度为零的点开始跑一个拓扑,(有点像集训第一天的T4那个题),从入度为零的点开始依次删点,删完之后图中就只会剩下环了。有一个区别就是奇数点链加环会被拓扑消解掉,偶数点链加环,它的环不会被消解,但它的环最多可以死的一个都剩,所以要与普通环区分一下

这个代码里说了很多废话,主要我想起玩过的一局狼人杀,奇迹商人板子,我不理解我明明四阶不低了,为什么还会碰到sb商人给幸运儿技能给的是枪,然后那sb幸运儿把我枪了说自己以为自己是猎人,就认为我是狼,我气炸了,我说我是纯白之女行了吧hhhhhhc

查看代码

//枪战Maf
//愿世界和平 阿门
//执枪的人,一定要做好被杀的觉悟 ---鲁鲁修 

//所以不杀人的都先给爷死hhhhc 
//只有杀人才有可能活下来是这意思
//当然成个环还是都得死
//所以世事纷纷恩怨何时了啊
//远了远了
//建图,没有入度不会死
//从没有入度的点开始,求最大他最后开枪
//求最小他第一个开枪
//有环最少要死一半(因为这个枪一定要响) ,最多死剩下一个,看谁枪快呗
//找环用并查集?
//一个时间只能有一位幸运儿开枪,问纯白之女有多大概率被幸运儿毙掉hhhhhc,商人狂喜 
//(我这辈子比较后悔的一件事大概就是误以为鲁鲁修是热血王道漫,结果全tm是刀,都给孩子刀傻了) 
//所以最大怎么处理啊啊啊啊啊啊啊啊啊啊啊 
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
const int mx=3000000+1000;
int n; 
int head[mx];
int len;
int an1,an2;//记录被杀还不如记录存话
int q[mx];
int l=1,r=0; 
bool vis[mx];//可能一个人被毙了一次后又被毙 
struct Node{
	int from;
	int to;
	int next; 
}e[mx];
int in[mx];//被杀 
int aim[mx];
int save[mx]; 
void Insert(int u,int v){
	e[++len].from=u;
	e[len].to=v;
	e[len].next=head[u];
	head[u]=len; 
}
void Solve(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		int x;
		scanf("%d",&x);
		aim[i]=x;
		in[x]++;
//		Insert(i,x);//不用建图啊,它一定是一指一的 
	} 
	for(int i=1;i<=n;++i){
		if(in[i]==0){
			an1++;
			an2++; 
			q[++r]=i; 
		/*	for(int j=head[i];j;j=e[j].next){//它不会死,它指的这个人必死
			//如果求最小,这个人指的所有人的in都要减1 
			//如果求最大不好搞啊,少死了几个也不好说 
			    q[++r]=e[j].to;//这些人是必死的 
				//这不又是前天t4的拓扑? 
			}*/
		}
		//那while得拿出来 
	
	}
	//printf()
	while(l<=r){
		int u=q[l];
		l++;
	//	for(int j=head[u];j;j=e[j].next){//这些人是必死的 
		int v=aim[u];
		if(vis[v]==1)continue;//这里真是又细又恶心 
		vis[v]=1; 
		int vv=aim[v];
		in[vv]--;
		save[vv]=1;
		if(in[vv]==0){
			//它能不死就不死
			an2++;//所以记录一下存活最大人数 
			q[++r]=vv;//我得用队列记录不死而不是死 
		//	printf("vv=%d r=%d\n",vv,r);
		}
	//	}
	} //最大不好求,算了,先放着 
	//此刻图中只会有环了,因为已经将所有入度为零的点和他指向的人
	//删了 
	//其实不用并查集,因为这个图极其简单 
	//用aim转回来就行了,其实也是建了个图 
//	printf("r=%d a2=%d\n",r,an2);
	int huan=0;
	for(int i=1;i<=n;++i){
		if(in[i]!=0 && vis[i]==0){
		//	printf("i=%d\n",i);
			int v=i;
			int pd=0;
			huan=0;
			while(1){
				if(vis[v]==1)break;
				vis[v]=1;
				v=aim[v];
				huan++;
				if(save[v]==1)pd=1;
			}
			if(pd==0 && huan>1)an1++;//这下没问题了 
		//	an1+=1;
		//这里有问题,就是,它可能一个都不活,
		//就是有这么一种情况,环外绝对活指向一个环
		//这会导致这个环被拆完,这好像没问题
		//不是这,an1不是直接加1,它有的人是因为
		// 瞄准他的人死了才进入环,他可能两个人指一个人 所以这个环里不能有人
		//被环外的人瞄准,an1才能+ 
			an2+=huan/2;//最多活一半真惨
			//是向下取整没有毛病 
		}
	}
	int d1=n-an1;
	int d2=n-an2;
	printf("%d %d\n",d2,d1); 
}

int main(){
//	freopen("data.in","r",stdin);
//	freopen("out.out","w",stdout);
	freopen("maf.in","r",stdin);
	freopen("maf.out","w",stdout);
	Solve();
	return 0;
}

T4

翻转游戏

这个题说实话属实是没有什么水平,当然我没做出来,我更没水平。用状压的思想去枚举虚拟第零行的状态,然后暴力修改,看是否符合。枚举完取个min最小修改次数。就是二进制运用的挺巧妙,我这代码抄的谁的我忘了

查看代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int mx=300;
char s[mx][mx];
int f[mx][mx];
int ans=1000000;
int n;
void updata(int x,int y){
	if(f[x][y]==1)f[x][y]=0;//这里那题解用 ^=1,属实是我水平不够了 
	else f[x][y]=1;
	if(f[x-1][y]==1)f[x-1][y]=0;
	else f[x-1][y]=1;
	if(f[x+1][y]==1)f[x+1][y]=0;
	else f[x+1][y]=1;
	if(f[x][y-1]==1)f[x][y-1]=0;
	else f[x][y-1]=1;
	if(f[x][y+1]==1)f[x][y+1]=0;
	else f[x][y+1]=1;//这么修改属实是暴力 
}
void dfs(int now,int step,int to){
	if(now==n+1){//深搜更新答案 
		for(int i=1;i<=n;++i){
			if(f[n][i]!=to)return ;
		} 
		ans=step<ans?step:ans;//三目运算真帅,以后要逐渐学了
		//通过枚举16种状态就解决了我疑惑的最小问题 
		return;
	}
	int pos=0;
	for(int i=1;i<=n;++i){
		if(f[now-1][i]!=to){
			step++;
			updata(now,i);
			pos|=1<<(i-1);
			//这个 或运算 在这里就记录的了我们在什么位置有修改
			//下面再还原只需要把pos再或回去就行 
			//其实还是非常妙的,二进制一定要多学 
			//当然我现在理解不太深刻,回头再学,一定学 
		} 
	} 
	dfs(now+1,step,to);
	for(int i=1;i<=n;++i){
		if(pos>>(i-1)&1)updata(now,i);
	} 
} 
void Solve(){
	char c;
	n=4;
	int pd=1;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			scanf(" %c",&s[i][j]);
			if(s[i][j]=='b')f[i][j]=1;
			else f[i][j]=0;
			if(i==1 && j==1)c=s[i][j];
			if(c!=s[i][j])pd=0;
		}
	}
	//通过调整下一行来更改上一行,最后一行决定行不行
	//同时第一行本身怎么换也要枚举,所以加一个第零行,一共有16种情况
	//依次来逐个修改,所以正好学学状压dp 
	if(pd==1){
		printf("0\n");
		return ;
	}
	for(int k=0;k<(1<<n);++k){//枚举第零行的所有状态 
		for(int i=1;i<=n;++i){
			f[0][i]=(k>>(i-1))&1?1:0;//这就把第0行的状态赋值出来了 
			//k右移i-1为就是这第i位,&1成立是1,否则0,二进制加三目运算太帅了 
		} 
		dfs(1,0,0);//一:第几行 二:第几步 三:目标是全转换成谁 
		dfs(1,0,1); 
	} 
	if(ans>=10000)printf("Impossible\n"); 
	else printf("%d\n",ans);
}
int main(){
	freopen("flip.in","r",stdin);
	freopen("flip.out","w",stdout);
	Solve(); 
	return 0;
} 

后记:

集训的快乐时光眨眼就过去了,之后就又是令人绝望的期末与文化课,唉,只要心中尚存有一丝希望,那便坚定不渝的追寻吧。纵使前路位置,一切渺茫,但总会有些东西,一直未变不是么

posted @ 2022-06-08 12:10  SMTwy  阅读(88)  评论(5编辑  收藏  举报