模拟赛 10-20考试记

10-20考试记

预期得分245,实际得分245。

第三题一看暴力那么好玩,直接各种分段暴力真开心。

rank_2,%%%AK大佬C。

第三题是很好的题嘞。

1、求助

(help.cpp/c/pas)

【问题背景】

马上就要noip了,lrt同志\(\displaystyle\begin{vmatrix}\textrm{慌}&\sqrt{\textrm{批}}\\\sqrt{\textrm{批}}&\textrm{的}\end{vmatrix}=慌得一批\),在某个透彻的晚四,找rqj整理OI知识点

【问题描述】

$\ \ \ \ \ \ \ $rqj可是个dalao,他有一个神奇的技能,可以把两个毫无关系的知识点完美的联系在一起,耗费时间 \(T_i\), lrt表示不服,但是他必须花好长时间才能做到,一次耗时\(t_i\ \ \ (t_i>T_i)\),对于整个OI知识网络,共有m种联系,每一种可以将两个知识点相连,现在二人要把整个OI知识联系起来,为了锻炼lrt,lrt必须自己想至少k个联系,但rqj日理万机,耐心有限,他希望这几次联系中,时间最长的那次联系的时间最小,但是rqj懒的算QAQ,于是来请聪明的你帮他算一算

【输入】

输入文件名\(help.in\)

第一行为三个整数\(n,k,m\),n为知识点数,m为联系个数,k如上述所示

接下来\(m\)行,每行四个整数\(x_i\ \ \ y_i\ \ \ T_i\ \ \ t_i\),

【输出】

输出仅一行,为最长联系时间的最小值

直接二分答案,每一次用克鲁斯卡尔判断图的连通性就好了。全场切?

code:

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

const int wx=2000017;

inline int read(){
	int sum=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
	while(ch>='0'&&ch<='9'){sum=(sum<<1)+(sum<<3)+ch-'0'; ch=getchar();}
	return sum*f;
}

int fa[wx],head=0x3f3f3f3f,tail=-0x3f3f3f3f;
int n,m,k,ans=0;

struct node{
	int x,y,d;
	friend bool operator < (const node & a,const node & b){
		return a.d<b.d;
	}
}t1[wx],t2[wx];

int find(int x){
	if(fa[x]==x)return x;
	return fa[x]=find(fa[x]);
}

void pre(){
	for(int i=1;i<=n;i++)fa[i]=i;
}

bool ok(int now){
	int l=1,r=m;
	int tot=0;
	while(l<=r){
		int mid=l+r>>1;
		if(t2[mid].d<=now)tot=mid,l=mid+1;
		else r=mid-1;
	}
	if(tot<k)return false;
	pre();
	int zmj=0;
	for(int i=1;i<=m;i++){
		if(t2[i].d>now)break;
		int fx=find(t2[i].x); int fy=find(t2[i].y);
		if(fx==fy)continue;
		fa[fx]=fy; zmj++;
		if(zmj>=n-1)return true;
	}
	for(int i=1;i<=m;i++){
		if(t1[i].d>now)break;
		int fx=find(t1[i].x); int fy=find(t1[i].y);
		if(fx==fy)continue;
		fa[fx]=fy; zmj++;
		if(zmj>=n-1)return true;
	}
	return false;
}

int main(){
	freopen("help.in","r",stdin);
	freopen("help.out","w",stdout);
	
	n=read(); k=read(); m=read();
	for(int i=1;i<=m;i++){
		int x,y; x=read(); y=read(); t1[i].d=read(); t2[i].d=read();
		t1[i].x=t2[i].x=x;
		t1[i].y=t2[i].y=y;
		head=min(head,min(t1[i].d,t2[i].d));
		tail=max(tail,max(t1[i].d,t2[i].d));
	}
	sort(t1+1,t1+1+m);
	sort(t2+1,t2+1+m);
	while(head<=tail){
		int mid=head+tail>>1;
		if(ok(mid))ans=mid,tail=mid-1;
		else head=mid+1;
	}
	printf("%d\n",ans);
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}

/*
4 2 5 
1 2 5 6 
1 3 1 3 
2 3 4 9 
2 4 1 6 
3 4 2 4
*/

2、心动

(olinr.cpp/c/pas)

【问题背景】

透彻人不干氨醛事,olinr走上了一条不归路

【问题描述】

$\ \ \ \ \ \ \ $olinr很喜欢TA,但是在二中的压迫下,透彻需谨慎,他必须合理安排在一起的时间,olinr和TA在不同
的教学楼,每天都要去不同的canteen打饭,他希望节约时间的前提下在一起走路的时间尽量长。

$\ \ \ \ \ \ \ $现在已知二人的教学楼和两个目标canteen还有二中的地图,有n个路口,m条路,每条路经过需要一
定的时间。

$\ \ \ \ \ \ \ $一句话概括题意:给出两个起点,终点,求最短路的最长公共路径

【输入】

输入文件名\(olinr.in\)

第一行为两个整数\(n\ ,\ m\),分别表示点数和边数

第二行为四个整数\(x_1\ \ \ y_1\ \ \ x_2\ \ \ y_2\) 分别表示两个教学楼和食堂的编号\(x_1\to y_1\ \ \ x_2\to y_2\)

接下来\(m\)行,每行三个整数\(x_i\ \ \ y_i\ \ \ z_i\) ,表示\(x_i\)\(y_i\)有一条长度为\(z_i\)的边

【输出】

输出仅一行,为最长公共路径

挺模板的一道题吧,两边SPFA标记好最短路上的边转移就好。

学长用枚举点对的\(n^2\)做法怒切,%%%。

code:

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>

#define int long long

using namespace std;

const int wx=1978;

inline int read(){
	int sum=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
	while(ch>='0'&&ch<='9'){sum=(sum<<1)+(sum<<3)+ch-'0'; ch=getchar();}
	return sum*f;
}

int num=1;
int n,m,val;
int sx,tx,sy,ty;

int head[wx],dis[wx],vis[wx];
int ans[wx],zmj[wx];

struct e{
	int nxt,to,dis,flag;
}edge[wx*wx];

void add(int from,int to,int dis){
	edge[++num].nxt=head[from];
	edge[num].to=to;
	edge[num].dis=dis;
	head[from]=num;
}

queue<int > q;
void jiade_Dij(int s){
	for(int i=1;i<=n;i++)dis[i]=0x3f3f3f3f,vis[i]=0;
	q.push(s);dis[s]=0;vis[s]=1;
	while(q.size()){
		int u=q.front(); q.pop(); vis[u]=0;
		for(int i=head[u];i;i=edge[i].nxt){
			int v=edge[i].to;
			if(dis[v]>dis[u]+edge[i].dis){
				dis[v]=dis[u]+edge[i].dis;
				if(!vis[v]){
					vis[v]=1; q.push(v);
				}
			}
		}
	}
}

void bfs(){
	q.push(sy); memset(vis,0,sizeof vis);
	vis[sy]=1;
	while(q.size()){
		int u=q.front(); q.pop();
		for(int i=head[u];i;i=edge[i].nxt){
			int v=edge[i].to;
			if(dis[v]+edge[i].dis==dis[u]){
				edge[i].flag=1;
				edge[i^1].flag=1;
				if(!vis[v]){
					vis[v]=1; q.push(v);
				}
			}
		}
	}
}

void work(){
	memset(vis,0,sizeof vis);
	vis[ty]=1; zmj[ty]=1; q.push(ty);
	while(q.size()){
		int u=q.front(); q.pop(); vis[u]=0;
		for(int i=head[u];i;i=edge[i].nxt){
			int v=edge[i].to;
			if(dis[v]+edge[i].dis==dis[u]){
				if(edge[i].flag&&ans[v]<ans[u]+edge[i].dis){
					ans[v]=ans[u]+edge[i].dis;
					val=max(val,ans[v]);
					if(!vis[v]){
						vis[v]=1;
						q.push(v);
					}
				}
				if(!zmj[v]){
					vis[v]=1;
					zmj[v]=1;
					q.push(v);
				}
			}
		}
	}
}

signed main(){
//	freopen("olinr.in","r",stdin);
//	freopen("olinr.out","w",stdout);
	
	n=read(); m=read();
	sx=read(); sy=read(); tx=read(); ty=read();
	for(int i=1;i<=m;i++){
		int x,y,z;
		x=read(); y=read(); z=read();
		add(x,y,z); add(y,x,z);
	}
	jiade_Dij(sx);
	bfs();
	jiade_Dij(tx);
	work();
	printf("%lld\n",val);
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}

3、透彻

(game.cpp/c/pas)

【问题背景】

奥赛部终于放假了, 像olinr这种人,当然选择透彻啦!

【问题描述】

$\ \ \ \ \ \ \ $olinr正在卧室的电脑前透彻。。。

$\ \ \ \ \ \ \ $olinr打算通关一个游戏,从而获得金币买装备。这个游戏有n个场景,有许多通关途径。具体来说,某些场景

可以通过分支到达其它场景,这些场景构成了一棵树。olinr此时在根节点,他要到叶子节点即可通关。每个场景

都有一些金币,olinr可以获得它们。由于太久没放假,olinr积累了大量的能力值,使得他可以同时通关\(k\)次,当

然,同一个场景再次走到就没有金币了QAQ(一个场景的金币只能获得一次),不过偷车时间宝贵,他急忙问

你,他最多能获得多少金币,你要在\(1s\) 内告诉他欧(1号节点为根节点)

【输入】

输入文件名game.in$

第一行为两个整数\(n\ ,\ k\),分别表示点数和次数

第二行为\(n\)个整数,分别表示每个场景的金币数量

接下来\(n-1\)行,每行两个整数\(x_i\ \ \ y_i\) ,表示\(x_i\)场景有一个分支通向\(y_i\)场景

【输出】

输出仅一行,为最大金币获得数量

考场上把45分暴力全拿了,都把我给写high了,但就是忘记想正解了。(其实不会)。

其实也很简单。 首先一直拿当前最大的的贪心是很容易证的。

问题就是每一次找到这个当前局面最大的。

考场上直接想的暴力。

也很容易想到用数据结构维护,那么用dfn序建树,维护区间最大值和最大值的位置,那么每一次更新之后可以直接查询\(t(1)\)的信息就好了。

个人感觉很好想,但是说不明白,哎呀不说了,直接上代码。

code:

#include <iostream>
#include <cstdio>

#define int long long

#define ls(o) o<<1
#define rs(o) o<<1|1

using namespace std;

const int wx=1000177;

inline int read(){
	int sum=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
	while(ch>='0'&&ch<='9'){sum=(sum<<1)+(sum<<3)+ch-'0'; ch=getchar();}
	return sum*f;
}

int num,n,k,ans,tot;
int vis[wx],tid[wx];
int head[wx],dfn[wx],size[wx],a[wx],f[wx];

struct val_tree{
	int l,r,ma,tag,pos;
	#define ma(o) t[o].ma
	#define tag(o) t[o].tag
	#define pos(o) t[o].pos
}t[wx*2];

struct node{
	int pos,ma;
};

void up(int o){
	if(ma(ls(o))>=ma(rs(o))){
		ma(o)=ma(ls(o)); pos(o)=pos(ls(o));
	}
	else ma(o)=ma(rs(o)),pos(o)=pos(rs(o));
}

void down(int o){
	if(tag(o)){
		ma(ls(o))+=tag(o); ma(rs(o))+=tag(o);
		tag(ls(o))+=tag(o); tag(rs(o))+=tag(o);
		tag(o)=0;
	}
}

void build(int o,int l,int r){
	t[o].l=l; t[o].r=r;
	if(l==r){pos(o)=l;return ;}
	int mid=t[o].l+t[o].r>>1;
	if(l<=mid)build(ls(o),l,mid);
	if(r>mid)build(rs(o),mid+1,r);
	up(o);
}

void update(int o,int l,int r,int k){
	if(l<=t[o].l&&t[o].r<=r){
		ma(o)+=k; tag(o)+=k; return ;
	}
	down(o); 
	int mid=t[o].l+t[o].r>>1;
	if(l<=mid)update(ls(o),l,r,k);
	if(r>mid)update(rs(o),l,r,k);
	up(o);
}

struct e{
	int nxt,to;
}edge[wx*2];

void add(int from,int to){
	edge[++num].nxt=head[from];
	edge[num].to=to;
	head[from]=num;
}

void dfs(int u,int fa){
	f[u]=fa; dfn[u]=++tot; tid[tot]=u; size[u]=1;
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(v==fa)continue;
		dfs(v,u); size[u]+=size[v];
	}
}

void dfs_wx(int u){
	while(!vis[u]&&u){
		update(1,dfn[u],dfn[u]+size[u]-1,-a[u]);
		vis[u]=1;
		u=f[u];
	}
}

signed main(){
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	n=read(); k=read();
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<n;i++){
		int x,y;
		x=read(); y=read();
		add(x,y); add(y,x);
	}
	dfs(1,0); build(1,1,n);
	for(int i=1;i<=n;i++)update(1,dfn[i],dfn[i]+size[i]-1,a[i]);
	while(k--){
		node tmp=(node){t[1].pos,t[1].ma}; 
		ans+=tmp.ma; int pos=tmp.pos;
		dfs_wx(tid[pos]);
	}
	printf("%lld",ans);
	return 0;
}

继续加油。

posted @ 2018-10-20 15:57  _王小呆  阅读(216)  评论(0编辑  收藏  举报