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发现是个大模拟,虽然字符串不太会用,但起码题意很好理解 边打代码 ...
随机推荐
- 大三上 —— IOS五天实训
第二天: 注册使用xib:1.首先为xib文件创建对象--let nib = UINib(nibName: "xib文件名", bundle: nil).2.具体的控件注册该xib ...
- reveal
链接 界面调试工具Reveal Reveal使用教程 iOS分析UI利器——Reveal及简单破解方法 Reveal使用步骤和 破解Revealapp的试用时间限制 end
- 数据表格 - DataGrid - 行编辑
行编辑一般用于单行数据的增删改,如果不用行编辑实现的话,对于表单数据量不大的情况,可以使用弹窗(Dialog),如果数据量比较大,也就是需要操作的数据比较多的时候,可以新开一个tab页. 新增/编辑 ...
-  非法字符 原因 以及解决办法
模板文件生成html文件之后会在body开头处加入一个可见的控制符 // 如果是Windows系统,修改为:$WIN = 1; $WIN = 0; ?> <!DOCTYPE html P ...
- MVC FormCollection collection
1.通过name获取值 collection.GetValues() 返回一个数组,适用于多选等,如果是单值可以[0] 2.直接转化为实体 将Action动作中传递的FormCollection转变成 ...
- usb驱动开发24之接口驱动
从第一节我们已经知道,usb_generic_driver在自己的生命线里,以一己之力将设备的各个接口送给了linux的设备模型,让usb总线的match函数,也就是usb_device_match, ...
- C#.NET 大型通用信息化系统集成快速开发平台 4.1 版本 - 忘记密码功能改进、手机短信、电子邮件
由于我们的系统接近有100000个用户账户,经常会有忘记密码的时候,用户多了,很小的一个功能,每天都会有很多人在用,每个功能都非常友善,会提高提系统的效率,提高用户体验. 一天最多能返回3次手机短信, ...
- knockoutJS学习笔记08:表单域绑定
前面的绑定都是用在基本标签上,这章主要讲表单域标签的绑定. 一.value 绑定 绑定标签:input text.textarea. <p>用户名:<input type=" ...
- javascript语法速查表
- SpringMVC @RequestBody接收Json对象字符串
其实 @RequestBody接收的是一个Json对象的字符串,而不是一个Json对象.然而在ajax请求往往传的都是Json对象,后来发现用 JSON.stringify(data)的方式就能将对象 ...