模拟赛 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;
}
继续加油。