HGOI20190707 题解
Problem A 钢铁侠的诞生
现在有$n$个数字$a_i \leq 10^9 $,然后取出$m$个数字,保证合法。
从小到大输出剩余的$n-m$个数字。
对于100%的数据$m\leq n \leq 3\times 10^5$
Sol : 直接map映射然后用iterator来遍历整个map输出答案即可。
复杂度大概是$O(n log_2 n)$
# pragma GCC optimize()
# include<bits/stdc++.h>
using namespace std;
int n,m;
map<int,int>mp;
int main()
{
scanf("%d%d",&n,&m);
for (int i=;i<=n;i++) {
int t; scanf("%d",&t);
mp[t]++;
}
for (int i=;i<=m;i++) {
int t; scanf("%d",&t);
mp[t]--;
}
map<int,int>::iterator it;
for (it=mp.begin();it!=mp.end();++it) {
pair<int,int>p=*it;
for (int j=;j<=p.second;j++) printf("%d ",p.first);
}
return ;
}
a.cpp
Problem B 钢铁侠的逃离
设函数$f(x) = $x的二进制中"1"的个数。 求 $\sum\limits_{i=1}^n f(B+i\times A)$ 的值。
对于100%的数据$A\leq 10^4 ,B \leq 10^{16} n\leq 10^{12} $
Sol : 对于每一个二进制位分别考虑,我们想法是对于二进制数第$k$位 $ B+A $和 $ B+(2^k+1)A $是相同的。
证明的话直接可以把两个数同时减去一个A,然后就变成了$B$和$B+(2^k)A$第$k$位的关系。
左式的$2^k A$相当于把A向左移k位,这个时候第k位一定是B的k位的数+0 = B的k位的数。 那么就不会发生变化。
所以我们只需要考虑一个循环节$B+A ... B+2^k A$中1的个数即可。
对于每个循环节,显然(也可以通过打表发现)每一位连续是1连续是0的可能比较大。所以我们考虑对于连续的1和连续的0一起处理。
设$f(a,b,k,r) = \sum\limits_{i=0} ^ {r} b+a\times i 中第k位1的个数 $
考虑如何算$f(a,b,k,r)$ . 首先我们需要在每一个01块内用O(1)的时间完成跳跃。
于是考虑什么时候是01的分界点。由于进位的问题所以每一个01块分界点一定是以$2^k$一个循环的,并且是$2^k$的倍数。
考虑一个位置 b 我们什么时候才能跳出这个块呢? 比该点更加靠右的$2^k$倍数的点可以轻易算出 $2^k \times (\left \lfloor \frac{b}{2^k} \right \rfloor\times +1 ) $ 就是下一个01分界点的坐标。
注意到01分界点事实上已经完成了进位即完成了01转换。
所以如果要跳到严格的下一个01分界点的右侧,我们需要跳跃$c= \frac{2^k \times (\left \lfloor \frac{b}{2^k} \right \rfloor +1 ) -1 }{a}+1$步
所以,对于$f(a,b,k,r)$的求法已经非常明确了:
$f(a,b,k,r) = \left\{\begin{matrix} b\ and \ 2^k = 0 \left\{\begin{matrix} 0 & r-c<0\\ \ \ \ f(a,b+c\times a,k,r-c)\ \ & r \geq c \end{matrix}\right.\\ b\ and \ 2^k = 1 \left\{\begin{matrix} r+1 & r-c<0\\ c+f(a,b+c\times a,k,r-c)& r \geq c \end{matrix}\right.\\ \end{matrix}\right.$
由于是从0开始的,所以n必须等于n-1 ,当前是从b+a开始的,所以是b=b+a
答案就是$ans = \sum\limits_{i=0}^{bit\_num(a+nb)} f(a,b,i,2^{i+1}-1)\times \left \lfloor \frac{n}{2^{i+1}} \right \rfloor+ f(a,b+\left \lfloor \frac{n}{2^{i+1}} \right \rfloor \times 2^{i+1} \times a,i,n \mod 2^{i+1})$
# include <bits/stdc++.h>
# define int long long
using namespace std;
int fun(int a,int b,int k,int r){
int d=b/(1ll<<k),c=((1ll<<k)*(d+)--b)/a+;
if(d&) return c>r?r+:c+fun(a,b+c*a,k,r-c);
else return c>r?:fun(a,b+c*a,k,r-c);
}
signed main()
{
int T; scanf("%d",&T);
while (T--) {
int a,b,n;
scanf("%lld%lld%lld",&a,&b,&n);
int ans=; b+=a; n--;
for (int i=;i<=;i++) {
int c=n/(1ll<<i+);
ans+=fun(a,b,i,(1ll<<i+)-)*c+fun(a,b+(1ll<<i+)*c*a,i,n-c*(1ll<<i+));
}
printf("%lld\n",ans);
}
return ;
}
b.cpp
Problem C 钢铁侠的复仇
设一个$N\times M$的矩阵,设$A_{i,j}$表示点$(i,j)$被攻克的难度而$B_{i,j}$ 表示点$(i,j)$被攻克的时间。
从$(i_1,j_1)$点转移到$(i_2,j_2)$点需要花费 $|i_1 - i_2|+|j_1-j_2|$ 的代价。
一条合法的攻克路径满足:经过路径上的所有点不重复而且不能经过$A_{i,j} = B_{i,j} = 0$的点。
输出沿着最长时间路径访问的时间。
对于100%的数据$ n,m \leq 10^3 $
Sol :本题是一个DP题目。
设$f_{i,j}$表示经过到点$(i,j)$结尾的路径的最大时间。
显然需要按照$A_{i,j}$值递增的顺序依次更新每个点。
转移方程就是$f_{i,j} = \max\limits_{k=1,w=1} ^ {k\leq n , w\leq m} [(A_{k,w} < A_{i,j}) f_{k,w}+|i-k|+|j-w|] + b_{i,j} $
复杂度 $O(n^2 m^2)$ get 30pts
# pragma GCC optimize()
# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N=1e3+;
struct rec{
int x,y,d;
};
int n,m;
struct cmp {
bool operator () (rec a,rec b) {
return a.d>b.d;
}
};
int a[N][N],b[N][N],f[N][N];
priority_queue<rec,vector<rec>,cmp>q;
signed main()
{
scanf("%d%d",&n,&m);
for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
scanf("%d",&a[i][j]),q.push((rec){i,j,a[i][j]});
for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
scanf("%d",&b[i][j]);
int ans=;
while (!q.empty()) {
rec u=q.top();q.pop();
if (a[u.x][u.y]==&&b[u.x][u.y]==) continue;
f[u.x][u.y]=b[u.x][u.y];
for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
if (a[i][j]!=&&a[i][j]<a[u.x][u.y]&&!(i==u.x&&j==u.y))
f[u.x][u.y]=max(f[u.x][u.y],f[i][j]+b[u.x][u.y]+abs(u.x-i)+abs(u.y-j));
ans=max(ans,f[u.x][u.y]);
}
printf("%d\n",ans);
return ;
}
c_30pts.cpp
考虑优化这个转移。
显然绝对值符号可以被拆,例如$(x,y)$在$(i,j)$左上角的时候($i \geq x , j \geq y$)时 有$f_{i,j} +| i - x | +| j - y | = f_{i,j}+i+j - x - y$
还有其他的三种情况不再赘述。
按照@hjc20032003的方法,可以先按$A_{i,j}$的大小把元素分成若干块(每一块里面的所有元素$A_{i,j}$值都相同)。
显然第k块中的$f_{i,j}$是k-1那一块的$f_{i,j}$转移而来。
我们先考虑$(x,y)$在$(i,j)$左上角的情况。
可以把相邻两块和并(由于当前行一定是从前一个可能的$A_{i,j}$转移而来的),并按照x坐标排序,然后从先到后依次扫,如果当前的元素$(x,y)$之前在前一个块(k-1)中那么插入到线段树第y号位子中。
如果当前元素在当前块$k-1$中那么利用当前线段树的元素查询在他左上角元素的信息来更新当前位置的f值$f_{x,y}$。
由于插入线段树的元素的$x$的坐标都是小于当前的元素的$x$坐标的,并且我们可以通过查询当前元素$y$之前区间信息而控制转移来源的点是在当前点的左上方的。
当然,由于可能从4个不同的方向转移,类似的操作要做4次(x轴翻转,y轴翻转,o点翻转,不翻转) 。 具体可以参考[Violet]天使玩偶/SJY摆棋子 的处理方法。
复杂度大概是$O(4 \times nm log_2 nm)$
#include<bits/stdc++.h>
#define GX(x,y) x=max(x,y)
#define ls(x) x<<1
#define rs(x) x<<1|1
#define A first
#define B second
#define mk make_pair
#define pb push_back
#define int long long
#define REP(i,s,t) for(int i=s;i<=t;i++)
using namespace std;
const int maxn=,inf=0x3fffffffffffffff;
typedef pair<int,int> pii;
vector<pii> vec[maxn*maxn];
int n,m,dis[maxn*maxn],a[maxn][maxn],b[maxn][maxn],f[maxn][maxn];
struct rec{int x,y; bool w;}q[maxn*maxn];
bool tag[maxn<<];
int maxx[maxn<<];
void push_down(int p){
if(tag[p]) tag[ls(p)]=tag[rs(p)]=true,maxx[ls(p)]=maxx[rs(p)]=-inf,tag[p]=false;
}
void _modify(int p,int l,int r,int tar,int val){
if(l==r){GX(maxx[p],val); return;}
int m=l+r>>;
push_down(p);
if(tar<=m) _modify(ls(p),l,m,tar,val);
else _modify(rs(p),m+,r,tar,val);
maxx[p]=max(maxx[ls(p)],maxx[rs(p)]);
}
void modify(int tar,int val){_modify(,,m,tar,val);}
int _query(int p,int l,int r,int nl,int nr){
if(nl<=l&&r<=nr) return maxx[p];
int m=l+r>>,ret=-inf;
push_down(p);
if(nl<=m) GX(ret,_query(ls(p),l,m,nl,nr));
if(m<nr) GX(ret,_query(rs(p),m+,r,nl,nr));
return ret;
}
int query(int nl,int nr){return _query(,,m,nl,nr);}
bool cmp1(rec a,rec b){
if(a.x!=b.x) return a.x<b.x;
return a.y<b.y;
}
signed main(){
// freopen("c.in","r",stdin);
// freopen("c.out","w",stdout);
scanf("%lld%lld",&n,&m);
int dis_cnt=;
REP(i,,n) REP(j,,m) scanf("%lld",&a[i][j]);
REP(i,,n) REP(j,,m) scanf("%lld",&b[i][j]);
REP(i,,n) REP(j,,m) if(a[i][j]) dis[++dis_cnt]=a[i][j];
sort(dis+,dis++dis_cnt);
int cnt=unique(dis+,dis++dis_cnt)-dis-;
REP(i,,n) REP(j,,m) if(a[i][j])
a[i][j]=lower_bound(dis+,dis+cnt+,a[i][j])-dis,vec[a[i][j]].pb(mk(i,j));
for(int i=;i<vec[].size();i++) f[vec[][i].A][vec[][i].B]=b[vec[][i].A][vec[][i].B];
REP(c,,cnt){
int pt=;
for(int i=;i<vec[c-].size();i++) q[++pt]=(rec){vec[c-][i].A,vec[c-][i].B,};
for(int i=;i<vec[c].size();i++) q[++pt]=(rec){vec[c][i].A,vec[c][i].B,};
sort(q+,q++pt,cmp1);
tag[]=true; maxx[]=-inf;
REP(i,,pt)
if(!q[i].w) modify(q[i].y,f[q[i].x][q[i].y]-q[i].x-q[i].y);
else GX(f[q[i].x][q[i].y],query(,q[i].y)+b[q[i].x][q[i].y]+q[i].x+q[i].y); tag[]=true; maxx[]=-inf;
for(int i=pt;i;i--) if(!q[i].w) modify(q[i].y,f[q[i].x][q[i].y]+q[i].x-q[i].y);
else GX(f[q[i].x][q[i].y],query(,q[i].y)+b[q[i].x][q[i].y]-q[i].x+q[i].y); tag[]=true; maxx[]=-inf;
REP(i,,pt) if(!q[i].w) modify(q[i].y,f[q[i].x][q[i].y]-q[i].x+q[i].y);
else GX(f[q[i].x][q[i].y],query(q[i].y,m)+b[q[i].x][q[i].y]+q[i].x-q[i].y); tag[]=true; maxx[]=-inf;
for(int i=pt;i;i--)
if(!q[i].w) modify(q[i].y,f[q[i].x][q[i].y]+q[i].x+q[i].y);
else GX(f[q[i].x][q[i].y],query(q[i].y,m)+b[q[i].x][q[i].y]-q[i].x-q[i].y);
}
int ans=;
REP(i,,n) REP(j,,m) GX(ans,f[i][j]);
cout<<ans<<endl;
return ;
}
c.cpp
但是,这样处理的常数非常大,由于绝对值符号的性质,按照上述四种转移方法只可能有一种转移方法是正确的。
由于绝对值的性质有$|i-x| \geq i-x$ 所以有
$ f_{i,j} + |i-x| + |j - y| = max\{ f_{i,j} + (i - x) + (j - y), f_{i,j} + (i - x) + (y - j), f_{i,j} + (x - i) + (j - y), f_{i,j} + (x - i) + (y - j)\} $ 成立。
所以转移的时候可以直接无视$i,x,j,y$的大小关系,一并转移即可。
转移方程是 $ f_{x,y}= max\{ -x - y + max(f_{i,j} + i + j), -x + y + max(f_{i,j} + i - j), x - y + max(f_{i,j} - i + j), x + y + max(f_{i,j} - i - j) ) $
其中$max(f_{i,j} + i + j) ... $是全局变量在每次转移完成后维护即可。
还要注意初始值不能加上坐标 , 即非0的最小的$A_{i,j}$所对应的$f_{i,j}$一开始从自己转移不能加上横纵坐标!!!
# include <bits/stdc++.h>
# define int long long
# define inf (0x3f3f3f3f3f3f3f3f)
using namespace std;
const int N=1e3+;
int f[N][N],b[N][N];
vector<pair<int,int> >v[N*N];
pair<int,int>t[N*N];
int mx1,mx2,mx3,mx4,ans,mint;
int n,m;
int Max(int a,int b,int c,int d) {
if (b>a) a=b;
if (c>a) a=c;
if (d>a) a=d;
return a;
}
void work(int r)
{
if (v[r].size()==) return;
int cnt=;for (int i=;i<v[r].size();i++) t[++cnt]=v[r][i];
int mxa=,mxb=,mxc=,mxd=;
for (int i=;i<=cnt;i++) {
int x=t[i].first,y=t[i].second;
if (r==) continue;
if (r!=mint) f[x][y]=Max(-x-y+mx1,-x+y+mx2,x-y+mx3,x+y+mx4)+b[x][y];
else f[x][y]=b[x][y];
ans=max(ans,f[x][y]);
mxa=max(mxa,f[x][y]+x+y); mxb=max(mxb,f[x][y]+x-y);
mxc=max(mxc,f[x][y]-x+y); mxd=max(mxd,f[x][y]-x-y);
}
mx1=max(mx1,mxa); mx2=max(mx2,mxb);
mx3=max(mx3,mxc); mx4=max(mx4,mxd);
}
signed main()
{
scanf("%lld%lld",&n,&m);
mint=inf;
for (int i=;i<=n;i++)
for (int j=;j<=m;j++) {
int t; scanf("%lld",&t);
if (t!=) mint=min(mint,t);
v[t].push_back(make_pair(i,j));
}
for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
scanf("%lld",&b[i][j]);
for (int a=;a<=;a++) work(a);
printf("%lld\n",ans);
return ;
}
c.cpp
HGOI20190707 题解的更多相关文章
- 2016 华南师大ACM校赛 SCNUCPC 非官方题解
我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...
- noip2016十连测题解
以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...
- BZOJ-2561-最小生成树 题解(最小割)
2561: 最小生成树(题解) Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1628 Solved: 786 传送门:http://www.lyd ...
- Codeforces Round #353 (Div. 2) ABCDE 题解 python
Problems # Name A Infinite Sequence standard input/output 1 s, 256 MB x3509 B Restoring P ...
- 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解
题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...
- 2016ACM青岛区域赛题解
A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Jav ...
- poj1399 hoj1037 Direct Visibility 题解 (宽搜)
http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...
- 网络流n题 题解
学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...
- CF100965C题解..
求方程 \[ \begin{array}\\ \sum_{i=1}^n x_i & \equiv & a_1 \pmod{p} \\ \sum_{i=1}^n x_i^2 & ...
随机推荐
- 协程+IO切换+小爬虫
from gevent import monkeymonkey.patch_all() import geventimport requests def f1(url): print(f'GET:{u ...
- Codeforces 1196E. Connected Component on a Chessboard
传送门 注意到棋盘可以看成无限大的,那么只要考虑如何构造一个尽可能合法的情况 不妨假设需要的白色格子比黑色格子少 那么容易发现最好的情况之一就是白色排一排然后中间黑的先连起来,剩下黑色的再全部填白色周 ...
- leetcode题库
leetcode题库 #题名题解通过率难度出现频率 1 两数之和 46.5%简单2 两数相加 35.5%中等3 无重复字符的最长子串 31.1%中等4 寻找两个有序数组的中位 ...
- 浏览器进程线程时间循环、与vue netTick的实现原理
浏览器事件循环(结合vue nextTick)https://juejin.im/post/5cb736c5f265da039955d4e8#comment messageChanel的讲解https ...
- Caffe中im2col的实现解析
这里,我是将Caffe中im2col的解析过程直接拉了出来,使用C++进行了输出,方便理解.代码如下: #include<iostream> using namespace std; bo ...
- idea安装完成后要做的几件事(设置字体、编码、行号)
1.设置字体大小和样式 打开设置:File-->Settings 看到如下界面,输入font,点击Editor目录下的Font设置字体大小和样式: Font:字体样式 size:字体大小 Fal ...
- js带有遮罩的弹窗
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- Mybatis分页-利用Mybatis Generator插件生成基于数据库方言的分页语句,统计记录总数 (转)
众所周知,Mybatis本身没有提供基于数据库方言的分页功能,而是基于JDBC的游标分页,很容易出现性能问题.网上有很多分页的解决方案,不外乎是基于Mybatis本机的插件机制,通过拦截Sql做分页. ...
- Linux进程管理工具之ps
1.PS进程管理指令 ps -aux USER:用户名称 PID:进程号 %CPU:进程占用CPU的百分比 %MEM:进程占用物理内存的百分比 VSZ:进程占用的虚拟内存大小(单位:KB) RS ...
- linux系统使用grep命令提取文件的基名或者路径名
效果等于~]#dirname /etc/sysconfig/network-scripts/ifcfg-ens33 echo "/etc/sysconfig/network-scripts/ ...