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(2)
# include<bits/stdc++.h>
using namespace std;
int n,m;
map<int,int>mp; 
int main()
{scanf("%d%d",&n,&m);for (int i=1;i<=n;i++) {int t; scanf("%d",&t);mp[t]++;}for (int i=1;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=1;j<=p.second;j++) printf("%d ",p.first);}return 0;
}
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+1)-1-b)/a+1;if(d&1) return c>r?r+1:c+fun(a,b+c*a,k,r-c);else return c>r?0: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=0; b+=a; n--; for (int i=0;i<=54;i++) {int c=n/(1ll<<i+1);ans+=fun(a,b,i,(1ll<<i+1)-1)*c+fun(a,b+(1ll<<i+1)*c*a,i,n-c*(1ll<<i+1));}printf("%lld\n",ans);  }return 0;
}
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(2)
# include <bits/stdc++.h>
# define int long long 
using namespace std;
const int N=1e3+10;
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=1;i<=n;i++)for (int j=1;j<=m;j++)scanf("%d",&a[i][j]),q.push((rec){i,j,a[i][j]});for (int i=1;i<=n;i++)for (int j=1;j<=m;j++)scanf("%d",&b[i][j]);int ans=0;while (!q.empty()) {rec u=q.top();q.pop();if (a[u.x][u.y]==0&&b[u.x][u.y]==0) continue;f[u.x][u.y]=b[u.x][u.y];for (int i=1;i<=n;i++)for (int j=1;j<=m;j++)if (a[i][j]!=0&&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 0;
}
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=1005,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<<2];
int maxx[maxn<<2];
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>>1;push_down(p);if(tar<=m) _modify(ls(p),l,m,tar,val);else _modify(rs(p),m+1,r,tar,val);maxx[p]=max(maxx[ls(p)],maxx[rs(p)]);
}
void modify(int tar,int val){_modify(1,1,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>>1,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+1,r,nl,nr));return ret;
}
int query(int nl,int nr){return _query(1,1,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=0;REP(i,1,n) REP(j,1,m) scanf("%lld",&a[i][j]);REP(i,1,n) REP(j,1,m) scanf("%lld",&b[i][j]);REP(i,1,n) REP(j,1,m) if(a[i][j]) dis[++dis_cnt]=a[i][j];sort(dis+1,dis+1+dis_cnt);int cnt=unique(dis+1,dis+1+dis_cnt)-dis-1;REP(i,1,n) REP(j,1,m) if(a[i][j])a[i][j]=lower_bound(dis+1,dis+cnt+1,a[i][j])-dis,vec[a[i][j]].pb(mk(i,j));for(int i=0;i<vec[1].size();i++) f[vec[1][i].A][vec[1][i].B]=b[vec[1][i].A][vec[1][i].B];REP(c,2,cnt){int pt=0;for(int i=0;i<vec[c-1].size();i++) q[++pt]=(rec){vec[c-1][i].A,vec[c-1][i].B,0};for(int i=0;i<vec[c].size();i++) q[++pt]=(rec){vec[c][i].A,vec[c][i].B,1};sort(q+1,q+1+pt,cmp1);tag[1]=true; maxx[1]=-inf;REP(i,1,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(1,q[i].y)+b[q[i].x][q[i].y]+q[i].x+q[i].y);tag[1]=true; maxx[1]=-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(1,q[i].y)+b[q[i].x][q[i].y]-q[i].x+q[i].y);tag[1]=true; maxx[1]=-inf;REP(i,1,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[1]=true; maxx[1]=-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=0;REP(i,1,n) REP(j,1,m) GX(ans,f[i][j]);cout<<ans<<endl;return 0;
}
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+10;
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()==0) return;int cnt=0;for (int i=0;i<v[r].size();i++) t[++cnt]=v[r][i];int mxa=0,mxb=0,mxc=0,mxd=0;for (int i=1;i<=cnt;i++) {int x=t[i].first,y=t[i].second;if (r==0) 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=1;i<=n;i++)for (int j=1;j<=m;j++) {int t; scanf("%lld",&t);if (t!=0) mint=min(mint,t);v[t].push_back(make_pair(i,j));     }for (int i=1;i<=n;i++)for (int j=1;j<=m;j++) scanf("%lld",&b[i][j]);for (int a=0;a<=1000000;a++) work(a);  printf("%lld\n",ans);  return 0;
}
c.cpp

 

转载于:https://www.cnblogs.com/ljc20020730/p/11146210.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/569216.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

scala初学之Tuple、Array、Map、文件操作入门实战

特此声明&#xff0c;本文中的代码 部分或全部来源王家林的scala教程&#xff1b;虽然王家林一直被大家所诟病&#xff0c;而且也无法确定这些scala的程序代码是不是他的。但是作为一个初学者觉得就算代码不是他的&#xff0c;他只是拿过来翻译一次&#xff0c;看他的视频也是能…

Java连载3-编译与运行阶段详解JRE,JDK,JVM关系

一、 1.JDK下载地址&#xff1a;https://www.oracle.com/technetwork/java/javase/downloads/jdk12-downloads-5295953.html 二、Java的加载与执行 1.Java程序运行包括&#xff1a; &#xff08;1&#xff09;编译阶段&#xff1a;检查Java源程序是否符合Java语法&#xff0c;符…

KMP算法NEXT数组纯手工生成

用一个实际的例子来说明&#xff0c;经历了看懂&#xff0c;看不懂&#xff0c;看懂&#xff0c;看不懂&#xff0c;看懂...后我终于决定把它记下来了。 例子字符串为&#xff1a;abaabaca 首先可以肯定&#xff0c;第一个位置永远位0&#xff0c;第二个位置永远为1.那么可以…

P1078 文化之旅

题面 这题好像是初二时老师讲过的一道题&#xff0c;但是。。我没听&#xff1f;&#xff1f;反正没交过就对了。。 我本来想的是深搜spfa&#xff0c;写到50行实现不了&#xff1f;&#xff1f;果断看tj&#xff0c;floyd&#xff1f;&#xff1f;&#xff1f;&#xff08;黑人…

KMP算法中NEXT数组的作用以及代码实现

在http://blog.csdn.net/u012613903/article/details/79004094中写到了如何手工去求一个NEXT数组&#xff0c;这个在很多考试中可以用来解题。但是在实际的使用中&#xff0c;NEXT数组究竟发挥着什么样的作用&#xff0c;如何用代码实现KMP算法呢&#xff1f; KMP算法是用来确…

最长公共连续子串

给出两个字符串&#xff08;可能包含空格&#xff09;,找出其中最长的公共连续子串,输出其长度。 注意这里是找连续子串。 算法&#xff1a;动态规划。f[i][j]表示第一个字符串前i个字符中与第二个字符串前j个中的最长连续子串长度 那么状态转移为&#xff1a;当s1(i)s2(j)时&a…

求最长回文串-从动态规划到马拉车之路(上)

要解决的问题&#xff1a; 给定一个字符串&#xff0c;要求求出这个字符串中的最长的回文串子串。 例子&#xff1a; cbddba的最长回文子串为 bddb cbdedba的最长回文子串为dbedb 由上面的例子可以看到&#xff0c;在考虑回文子串的问题时需要考虑奇偶性。因为奇回文关于中…

为什么Python中称__lt__、__gt__等为“富比较”方法

Python中基类object提供了一系列可以用于实现同类对象进行“比较”的方法&#xff0c;可以用于同类对象的不同实例进行比较&#xff0c;包括__lt__、__gt__、__le__、__ge__、__eq__和__ne__六个方法。 那么为什么叫“富比较”(“rich comparison”)方法呢&#xff1f;查了相关…

求最长回文串-从动态规划到马拉车之路(下)

预备知识&#xff1a; &#xff08;1&#xff09;在一个数轴上有两点i和j&#xff08;i<j&#xff09;关于点m对称&#xff0c;那么有 i 2m-j&#xff1b; 证明&#xff1a; 因为 i<j 且 i 和 j 关于 m 对称&#xff0c;那么有 &#xff08;i j&#xff09;/ 2 m 所…

项目管理实战之团队管理 (转自:zhuweisky)

一个系统不仅需要优秀的分析和设计&#xff0c;更需要一个良好的过程将其从蓝图转化为实现。这个过程中最重要的是对团队的管理&#xff0c;也就是人的管理。一个优秀的团队和一个糟糕的团队的效能是天壤之别&#xff0c;她们之间的比例不是1:100或1:1000这样量化的数字能够表示…

python3 内置方法

# -*- coding:utf-8 -*- # Author: Evan Mi import functools # 取绝对值 print(abs:, abs(-1)) # 如果一个可迭代对象的所有元素都为真&#xff0c;返回true ;空也返回真 print(all:, all([1, 0, -3])) # 有一个为真就全为真 print(any:, any([1, 0, -1])) # 变成可打印的字符…

JS 职责链模式

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title></head><script>/*职责链模式 所有对象依次处理请求&#xff0c;1不能处理传给2&#xff0c;2不能处理传给3....*//*场景 打折 100以下不打折&…

python3字符串常用操作

# -*- coding:utf-8 -*- # Author: Evan Miname "my name is alex"print(name.capitalize()) # 首字母大写 print(name.count("a")) # 统计整个字符中a的个数 print(aaaaa.count("a", 0, len(aaaaa)-1)) # 前闭后开 print(name.center(50, &…

通过NGINX location实现一个域名访问多个项目

location ~ \.php$ {   root /home/webroot; //此目录下有多个项目 project1 &#xff0c;project2...   fastcgi_pass $php_upstream;   fastcgi_index index.php;   include fastcgi.conf; } location ~/project1 {   index index.php;   fastcgi_pass $php_u…

python3 set相关操作

# -*- coding:utf-8 -*- # Author: Evan Mi# 创建一个set list_1 [1, 3, 5, 7, 3, 6, 7, 9] list_1 set(list_1) list_2 set([2, 6, 0, 66, 22, 8, 4]) print(list_1, type(list_1))# 交集 print(list_1.intersection(list_2)) print(list_1 & list_2) # 并集 print(lis…

JDK环境变量

下载打开如下链接&#xff1a;http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html &#xff0c;进入JDK1.8下载官网&#xff0c;或者直接百度JDK1.8&#xff0c;也可进入下载官网。 进入官网后&#xff0c;按照所给信息下载对应系统版本的J…

python3字典相关方法

# -*- coding:utf-8 -*- # Author: Evan Miinfo {stu1101: TengLan Wu,stu1102: LongZe Luola,stu1103: XiaoZe Maliya } # 字典是无序的 print(info) print(info[stu1101]) # 不存在会报错 print(info.get(stu1101)) # 不存在返回None print(stu1103 in info) # 判断是否…

Shadow Defender 语言文件并注册

:: ::关闭回显 echo off ::设置标题 title 覆盖 Shadow Defender 语言文件并注册:: ::获取管理员权限 set "_FilePath%~f0" set "_FileDir%~dp0" setlocal EnableExtensions EnableDelayedExpansion fltmc >nul 2>&1 || (echo Set UAC CreateOb…

python3 列表相关操作

# -*- coding:utf-8 -*- # Author: Evan Mi import copynames ["ZhangYang", "Guyun", "XiangPeng", "XuliangChen"] #创建一个列表 names.append("LeiHaiDong") # 给列表的末尾追加元素 names.insert(1, "ChenRongHu…

NickLee 多層菜單

void InitMenu(){ //初始化菜單 MenuItem menuFirst; DataSet dsPermit; UserInfo ui ; DataSet dsFfunc.GetDataTable("select * from cqsSystemTree where F_Parent000 and isValid1 order by showSort"); foreach (DataRow myrow in dsF.Tables[0].Rows…