CH Round #56 - 国庆节欢乐赛解题报告
最近CH上的比赛很多,在此会全部写出解题报告,与大家交流一下解题方法与技巧。
T1 魔幻森林
描述
Cortana来到了一片魔幻森林,这片森林可以被视作一个N*M的矩阵,矩阵中的每个位置上都长着一棵树,其中一些树上结有能够产生能量的魔力水果。已知每个水果的位置(Xi,Yi)以及它能提供的能量Ci。
然而,魔幻森林在某些时候会发生变化:
(1) 有两行树交换了位置。
(2) 有两列树交换了位置。
当然,树上结有的水果也跟随着树一起移动。不过,只有当两行(列)包含的魔力水果数都大于0,或者两行(列)都没有魔力水果时,上述变化才会发生。
Cortana对这些魔力水果很感兴趣,希望你帮她实时监测这片森林里魔力水果的情况。
输入格式
第一行包含3个整数N,M,K,分别表示矩阵的行数、列数和魔力水果的总数。
接下来K行每行三个整数X,Y,C,描述水果的位置(X,Y)和能量C。如果两个水果的位置相同,它们的能量累加。
第K+2行有一个整数T。
接下来T行每行三个整数Q,A,B:
若Q=1,表示A、B两行交换;
若Q=2,表示A、B两列交换;
若Q=3,表示询问位置(A,B)上产生的能量(没有水果视为0)。
1<=N,M<=2*10^9,0<=K,T<=10^5,0<=X,A<=N-1,0<=Y,B<=M-1,1<=C<=1000。
输出格式
对于每个询问,输出一个整数表示答案。
看到如此大的范围,果子又如此少,我一开始想到的是稀疏矩阵。然后思考如何交换。后来发现离散化即可。每次交换行或者交换列就将离散化后的坐标交换即可。用map实现,所有操作log(n),核心代码不超过20行
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <list>
#include <vector>
#include <ctime>
#include <functional>
#define pritnf printf
#define scafn scanf
#define For(i,j,k) for(int i=(j);i<=(k);(i)++)
using namespace std;
typedef long long LL;
typedef unsigned int Uint;
const int INF=0x7ffffff;
//==============struct declaration============== //==============var declaration=================
const int MAXN=;
map <int,int> R,C;
map <pair<int,int>,int> Fruits;
int row,col,k,q;
int rcnt=,ccnt=;
//==============function declaration============ //==============main code=======================
int main()
{
scanf("%d%d%d",&row,&col,&k);
For(i,,k){
int r,c,v;
scanf("%d%d%d",&r,&c,&v);
if (R[r]==) R[r]=++rcnt;
if (C[c]==) C[c]=++ccnt;
Fruits[make_pair(R[r],C[c])]+=v;
}
scanf("%d",&q);
while (q--){
int cmd,a,b;
scanf("%d%d%d",&cmd,&a,&b);
if (cmd==){
if (R[a]==||R[b]==) continue;
swap(R[a],R[b]);
}
else if (cmd==){
if (C[a]==||C[b]==) continue;
swap(C[a],C[b]);
}
else if (cmd==){
printf("%d\n",Fruits[make_pair(R[a],C[b])]);
}
} return ;
}
//================fuction code====================
T1代码
T2 过河
描述
有个青蛙要过河,河的横截面被看做一个数轴,河岸在0和M处。河里有N块石头,可以用数轴上(0,M)中的N个点表示。青蛙要从0跳到M,每步跳的距离不能超过L,并且必须落在石头上(不能跳进河里)。
以目前的石头数,青蛙有可能过不了河。Cortana想帮帮这只青蛙,但是又不想让它过河过得太容易(以免养成青蛙好吃懒做的坏习惯),于是决定往河里再添加一些石头,使得青蛙在能过河的前提下,过河需要跳的步数尽可能多。
由于这只青蛙也很机智,在Cortana放好石头以后,它总会采取最优策略(跳尽可能少的步数)过河。因此她Cortana要采取适当的方式放置石头,使青蛙的最优策略尽可能差。
输入格式
第一行3个正整数N、M、L。
第二行N个正整数Ai,描述河里已经有的石头的位置。
0<=N<=2*10^5,1<=M<=10^9,1<=L<=10^9,0<Ai<M。
输出格式
输出在Cortana添加一些石头后,过河的最优策略的步数最大是多少。
由于青蛙跳的步数没有下限,我们知道过河的最优策略一定是跳到能跳到的离当前石头最远的石头上,如果青蛙能够向前跳,那么没有必要增加石头。下面主要考虑不能够跳到的情况下如何加石头。
设当前位置是pos,上一步跳的距离为Laststep,在pos+L的范围内没有石头。显然加石头加的越近越好,我们考虑加石头在pos+L-Laststep+1的位置。
为什么?因为如果再向前1格,加在pos+L-Laststep的地方,青蛙会直接从pos-Laststep的地方直接跳到这里,而不会经过pos位。再向后一格的话解不会比当前格更优。
然后Laststep在每次跳动的时候进行修改。特别地,Laststep初始应该为l,这样青蛙如果出门的时候没办法直接跳到石头上就会跳至1。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <list>
#include <vector>
#include <ctime>
#include <functional>
#define pritnf printf
#define scafn scanf
#define For(i,j,k) for(int i=(j);i<=(k);(i)++)
using namespace std;
typedef long long LL;
typedef unsigned int Uint;
const int INF=0x7fffffff;
//==============struct declaration============== //==============var declaration=================
const int MAXN=*;
int n,m,l;
int Stone[MAXN];
//==============function declaration============ //==============main code=======================
int main()
{
scanf("%d%d%d",&n,&m,&l);
For(i,,n)
scanf("%d",Stone+i);
sort(Stone+,Stone++n);
Stone[]=;Stone[n+]=m;Stone[n+]=INF;
int Laststep=l,pos=,cnt=;
while (Stone[pos]<m){
int i;
for(i=;Stone[i+pos]-Stone[pos]<=l;i++)
Laststep=Stone[i+pos]-Stone[pos];
if (i!=)
pos=pos+i-;
else{
Stone[pos]+=l-Laststep+;
Laststep=l-Laststep+;
}
cnt++;
//pritnf("%d ",Stone[pos]);
}
printf("%d\n",cnt);
return ;
}
//================fuction code====================
T2代码
T3 异象石
描述
Adera是Microsoft应用商店中的一款解谜游戏。
异象石是进入Adera中异时空的引导物,在Adera的异时空中有一张地图。这张地图上有N个点,有N-1条双向边把它们连通起来。起初地图上没有任何异象石,在接下来的M个时刻中,每个时刻会发生以下三种类型的事件之一:
1. 地图的某个点上出现了异象石(已经出现的不会再次出现);
2. 地图某个点上的异象石被摧毁(不会摧毁没有异象石的点);
3. 向玩家询问使所有异象石所在的点连通的边集的总长度最小是多少。
请你作为玩家回答这些问题。
输入格式
第一行有一个整数N,表示点的个数。
接下来N-1行每行三个整数x,y,z,表示点x和y之间有一条长度为z的双向边。
第N+1行有一个正整数M。
接下来M行每行是一个事件,事件是以下三种格式之一:
+ x 表示点x上出现了异象石
- x 表示点x上的异象石被摧毁
? 表示询问使当前所有异象石所在的点连通所需的边集的总长度最小是多少。
1 ≤ n, m ≤ 10^5, 1 ≤ x, y ≤ n, x ≠ y, 1 ≤ z ≤ 10^9
输出格式
对于每个 ? 事件,输出一个整数表示答案。
个人认为非常好的一道题。
一棵树,给你一些动态变化的点,要求求出链接这些点的最短道路是多少。
对于无根树,我们经常可以选定一个根节点化为有根树。
以1节点为根,进行dfs,得到每个点的dfs编号。
把有异象石的节点按dfs序从小到大排好序,答案就是该序列中相邻两个数的距离和。
这么说答案可能还是不容易求,那么用这个办法:求出每个点和它的前驱、后继(最小点的前驱是最大点,最大点的后继是最小点)的点的距离,这个距离之和就是答案的两倍。
由于篇幅原因,在此不给出证明。<del>其实我不会证</del>
画图很容易看出。
维护这个序列set可以很容易地完成,求出距离可以用LCA。所有操作均为log(n)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <list>
#include <vector>
#include <ctime>
#include <functional>
#define pritnf printf
#define scafn scanf
#define For(i,j,k) for(int i=(j);i<=(k);(i)++)
using namespace std;
typedef long long LL;
typedef unsigned int Uint;
const int INF=0x7ffffff;
//==============struct declaration==============
struct adj{
int to,dist;
adj(int to=,int dist=):to(to),dist(dist){}
};
//==============var declaration=================
const int MAXN=;
vector <adj> Edge[MAXN];
int depth[MAXN],pa[][MAXN],n,q,maxd=-,pre[MAXN],no[MAXN],Index=;
LL dist[][MAXN],ans=;
set <int> Stone;
//==============function declaration============
void dfs(int x);
void init();
LL lca(int u,int v);
set <int>::iterator L(set <int>::iterator it);
set <int>::iterator R(set <int>::iterator it);
//==============main code=======================
int main()
{
scanf("%d",&n);
For(i,,n-){
int s,e,t;
scanf("%d%d%d",&s,&e,&t);
Edge[s].push_back(adj(e,t));
Edge[e].push_back(adj(s,t));
}
memset(pa,-,sizeof(pa));
memset(dist,,sizeof(dist));depth[]=;
dfs();init();
scanf("%d",&q);
set <int>::iterator it,itl,itr;
while (q--){
char cmd;int k;
scanf("%c",&cmd);
while (cmd!='+'&&cmd!='-'&&cmd!='?')
scanf("%c",&cmd);
if (cmd=='+'){
scanf("%d",&k);
Stone.insert(pre[k]);
it=Stone.find(pre[k]);
ans+=lca(*L(it),*it);
ans+=lca(*R(it),*it);
ans-=lca(*R(it),*L(it));
}
else if (cmd=='-'){
scanf("%d",&k);
it=Stone.find(pre[k]);
ans-=lca(*L(it),*it);
ans-=lca(*R(it),*it);
ans+=lca(*R(it),*L(it));
Stone.erase(it);
}
else if (cmd=='?')
printf("%lld\n",ans/);
}
return ;
}
//================fuction code====================
void dfs(int x)
{
pre[x]=++Index;
no[Index]=x;
int siz=Edge[x].size()-;
For(i,,siz){
adj &e=Edge[x][i];
if (depth[e.to]==){
depth[e.to]=depth[x]+;
pa[][e.to]=x;
dist[][e.to]=e.dist;
dfs(e.to);
}
}
maxd=max(maxd,depth[x]);
}
void init()
{
for(int i=;(<<i)<=maxd;i++)
For(x,,n){
int r=pa[i][x];
if (r<)
pa[i][x]=-;
else{
pa[i+][x]=pa[i][r];
dist[i+][x]=dist[i][x]+dist[i][r];
}
}
}
set <int>::iterator L(set <int>::iterator it)
{
if (it==Stone.begin())
return --Stone.end();
return --it;
}
set <int>::iterator R(set <int>::iterator it)
{
if (it==--Stone.end())
return Stone.begin();
return ++it;
}
LL lca(int u,int v)
{
LL res=;
u=no[u];v=no[v];
if (depth[u]<depth[v])//保证u在下面
u^=v^=u^=v;
for(int i=;(<<i)<=depth[u]-depth[v];i++)
if ((depth[u]-depth[v])&(<<i)){
res+=dist[i][u];
u=pa[i][u];
}
if (u==v) return res;
for(int i=;i>=;i--)
if (pa[i][v]!=pa[i][u]){
res+=dist[i][u]+dist[i][v];
u=pa[i][u];v=pa[i][v];
}
return res+dist[][v]+dist[][u];
}
T3代码
比赛网址:http://ch.ezoj.tk/contest/CH%20Round%20%2356%20-%20%E5%9B%BD%E5%BA%86%E8%8A%82%E6%AC%A2%E4%B9%90%E8%B5%9B
CH Round #56 - 国庆节欢乐赛解题报告的更多相关文章
- ZROIDay4-比赛解题报告
ZROIDay4-比赛解题报告 扯闲话 感觉这个出题人的题做起来全都没感觉啊,今天又凉了,T1完全不知道什么意思,T2只会暴力,T3现在还不懂什么意思,真的太菜了 A 题意半天没搞懂爆零GG了,讲了一 ...
- ZROIDay3-比赛解题报告
ZROIDay3-比赛解题报告 瞎扯 从今天开始考试有点不在状态,可能是因为不太适应题目的原因,T1已经接近了思想但是没有想到状态转移,T2思考方向错误,T3不会打LCT,还是太菜了 A 考场上想到要 ...
- Facebook Hacker Cup 2014 Qualification Round 竞赛试题 Square Detector 解题报告
Facebook Hacker Cup 2014 Qualification Round比赛Square Detector题的解题报告.单击这里打开题目链接(国内访问需要那个,你懂的). 原题如下: ...
- 10.30 NFLS-NOIP模拟赛 解题报告
总结:今天去了NOIP模拟赛,其实是几道USACO的经典的题目,第一题和最后一题都有思路,第二题是我一开始写了个spfa,写了一半中途发现应该是矩阵乘法,然后没做完,然后就没有然后了!第二题的暴力都没 ...
- 2018.10.26NOIP模拟赛解题报告
心路历程 预计得分:\(100 + 100 + 70\) 实际得分:\(40 + 100 + 70\) 妈妈我又挂分了qwq..T1过了大样例就没管,直到临考试结束前\(10min\)才发现大样例是假 ...
- 20201101gryz模拟赛解题报告
写在前面 2020rp++ 停课的第一场模拟赛 拿上一年的上一年的day1来考的, 结果得分期望220pts,实际135pts,rank3,太菜了 考着考着机房灯突然灭了,当时慌的一批 以为断电代码要 ...
- [ACM]2013山东省“浪潮杯”省赛 解题报告
题目地址:http://acm.upc.edu.cn/problemset.php?page=13 2217~2226 A.Rescue The Princess 一个等边三角形告诉前2个点,求逆时 ...
- 20201115gryz模拟赛解题报告
写在前面 T1:期望100pts,实际0pts(7:50 ~ 8:50 T2:期望0pts,实际0pts(10:00 ~ 10:35 T3:期望20pts,实际40pts( 9:10 ~ 10:00, ...
- 20201102gryz模拟赛解题报告
简述我的苦逼做题经历 考的是NOIP2017day1原题, 开始看到小凯的疑惑时感觉特水,因为这题初中老师讲过, 很nice的秒切 T2发现是个大模拟,虽然字符串不太会用,但起码题意很好理解 边打代码 ...
随机推荐
- Linux基础 - scp免密码登陆进行远程文件同步
在工作中经常有遇到需要脚本自动化同步文件的地方,比如数据库异地备份.假设有两台机子A(192.168.16.218)和B(192.168.16.117),需要能够让A免密码连接B. 先来看看正常的ss ...
- 【ASP.NET实战教程】基于ASP.NET技术下多用户博客系统全程实战开发(NNblog)
岁末主推:牛牛老师主讲,多用户博客系统,基于ASP.NET技术,年后将带来移动业务平台项目项目目标: 打造个性品牌Blogo,定制多用户博客 为每一个博客用户提供个性化的 blogo解决方案,打造精品 ...
- AppBox升级进行时 - 拥抱Entity Framework的Code First开发模式
AppBox 是基于 FineUI 的通用权限管理框架,包括用户管理.职称管理.部门管理.角色管理.角色权限管理等模块. 从Subsonic到Entity Framework Subsonic最早发布 ...
- C#、ASP.NET获取当前应用程序的绝对路径,获取程序工作路径 (转帖)
C#.ASP.NET获取当前应用程序的绝对路径,获取程序工作路径 ============================================ 使用 Application.Start ...
- Coding 及 git 的工程使用方法
在过去的两周,同学们除了在学习 C 语言之外,还在学习如何利用 git 将自己的代码上传到 coding 中.也有大量的同学,成功的上传了代码.但是,实际上大部分同学的用法都不合理.这里,以一 ...
- Android 线程池(转)
new Thread的弊端及Java四种线程池的使用: 1.new Thread的弊端 ); } catch (InterruptedException e) { // TODO Auto-gener ...
- linux下ftp的配置
最近公司要用到ftp,小菜鸡百度了一下教程,自己也总结一下 现在随便百度都是vsftpd的服务,所以这里我也是用vsftp 1.检测或安装vsftp 首先检查一下你的主机是否含有vsftp服务,关于r ...
- C#稳固基础:传统遍历与迭代器
Hello,Coders.我们除了天天的码 if…else…之外,还会不断的码出foreach.我今天要说的是:传统遍历需实现的接口及我们还有一种更简洁优雅的方式实现多种迭代器. 传统遍历 传统的遍历 ...
- php集成动态口令认证
这篇文章主要为大家详细介绍了php集成动态口令认证,动态口令采用一次一密.用过密码作废的方式来提高安全性能,感兴趣的小伙伴们可以参考一下 大多数系统目前均使用的静态密码进行身份认证登录,但由于静态密码 ...
- Servlet和JSP学习指导与实践(三):JSP助阵
前言: JSP(Java Server Page)虽然作为一门服务端的语言,但它并没有创新新的语言标准.有些人一接触jsp之后发现易学易懂.实际上,jsp的内部原理仍然是基于Servlet,它是Ser ...