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发现是个大模拟,虽然字符串不太会用,但起码题意很好理解 边打代码 ...
随机推荐
- PAT 1041. 考试座位号(15)
每个PAT考生在参加考试时都会被分配两个座位号,一个是试机座位,一个是考试座位.正常情况下,考生在入场时先得到试机座位号码,入座进入试机状态后,系统会显示该考生的考试座位号码,考试时考生需要换到考试座 ...
- JSP之WEB服务器:Apache与Tomcat的区别 ,几种常见的web/应用服务器
注意:此为2009年的blog,注意时效性(针对常见服务器) APACHE是一个web服务器环境程序 启用他可以作为web服务器使用 不过只支持静态网页 如(asp,php,cgi,jsp)等 ...
- 字符加密Cipher(bzoj 1031)
Description 喜欢钻研问题的JS同学,最近又迷上了对加密方法的思考.一天,他突然想出了一种他认为是终极的加密办法 :把需要加密的信息排成一圈,显然,它们有很多种不同的读法.例如下图,可以读作 ...
- JSP中ResultSet的方法
1,如何获得ResultSet的结构 ResultSetMetaData rsmd=rs.getMetaData();cn=rsmd.getColumnCount();for(int ik=1;ik& ...
- JS组件系列——Bootstrap Select2组件使用小结
前言:在介绍select组件的时候,博主之前分享过一篇JS组件系列——两种bootstrap multiselect组件大比拼,这两个组件的功能确实很强大,只可惜没有图文结合的效果(也就是将图片放入到 ...
- 基于FPGA的飞机的小游戏
基于FPGA的飞机的小游戏 实验原理 该实验主要分为4个模块,采用至上而下的设计方法进行设计.由50M的晶振电路提供时钟源,VGA显示控制模块.图形显示控制模块.移动模块的时钟为25M,由时钟分频电路 ...
- php设置浏览器响应时间
ini_set('max_execution_time', '0'); ‘0’表示不受时间限制,一般默认30s;
- ubuntu16.04装MatConvNet
按matconvnet官网上的步骤来,编译代码的时候会发现编译失败. 参考这条issues 以下是我的解决方案: I use ubuntu16.04 with x64 architecture. I ...
- Java构造方法
- sublime2使用jshint
合理配置Jshint可以帮助写出高质量的代码,通过sublime2插件 JSHint Gutter 可以迅速提供开发效率和减少bug的个数. 1.安装JSHint Gutter插件 sublime2按 ...