列表

详情


NC24901. 修仙时在做什么?有没有空?可以来炼丹吗?

描述

众所周知,杨大佬很强(从名字就可以看出来了),但他不仅仅满足于此,还想要变得更强,所以他每天都在爆肝修仙打cf。然而过了一段时间,杨大佬发现,他已经变得反手无力,正手不精,脚步松散,反应迟钝,肝也爆不了,仙也修不动,有时甚至会产生“1分钟只有59秒”这样的错觉。为了恢复往日的雄风,杨大佬采取了一系列措施——比如在修仙的时候顺便炼些丹药拿来补肝,于是很快,杨大佬又变得和过去一样健康了。

恢复了精力的杨大佬,开始思考起了一个小问题。为了能正确的描述这个问题,让我们先来回顾一些背景知识。

每种丹药都有一个非负整数能力值,杨大佬在炼丹时,每次可以花费一些仙气对某个丹药的能力值进行修改。具体来说,如果有个丹药的能力值是x,而杨大佬想将这个丹药的能力值变成,则他需要花费的仙气值fairy(x,i)为:



其中指按位异或。

现在考虑两个能力值不同的丹药,设他们的能力值分别为u和v。根据上一段关于fairy函数的描述不难知道,如果杨大佬想将能力值为u的丹药修改成能力值为v的丹药,那么他必然是按照一定的顺序轮流修改非负整数u的每一个二进制位,直到将其修改成v,在这一过程中杨大佬花费的总仙气值就是每次修改单一二进制位所花费的仙气值(fairy值)之和。显然,在将u修改成v的所有操作顺序中,应存在一个顺序使得杨大佬花费的总仙气最少,我们记这个最小仙气花费值为angel(u,v)。

有了fairy(x,i)和angel(u,v)的定义后,我们终于可以毫无偏差地描述杨大佬的小问题了。杨大佬手上有n个丹药,其中第i个丹药的能力值为ai。现在,杨大佬想将某个丹药的能力值变成除它本身外的其他某个丹药的能力值。杨大佬想知道的是,他最少需要花费多少仙气才能达成这一目的,也就是说杨大佬想知道下面这个式子的答案:



你能帮杨大佬解决这个问题吗?

输入描述

第一行是一个整数,表示杨大佬拥有的丹药个数。
接下来n行,每行一个整数,给出了每个丹药的能力值。

输出描述

输出一行一个整数ans,表示的值。

示例1

输入:

5
23
76
51
93
42

输出:

2071072

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++14(g++5.4) 解法, 执行用时: 581ms, 内存消耗: 15592K, 提交时间: 2019-04-16 20:09:43

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=5e5+5; 
int a[maxn];
typedef long long ll;
ll dis[maxn],n;
struct node{
	int id;
	ll dis;
	bool operator<(const node&a)const{
		return dis>a.dis;
	}
};
const int mod=19260817;
priority_queue<node>Q;
int mark[maxn];
int qpow(int a,int b){
	int c=1;
	while (b){
		if (b&1)
			c=1ll*a*c%mod;
		b>>=1;
		a=1ll*a*a%mod;
	}
	return c;
}
int get(int u,int v,int q){
	return qpow(max(u,v),1<<q)+1;
}
int main(){
	memset(dis,0x3f3f3f,sizeof(dis));
	memset(mark,-1,sizeof(mark));
	cin >> n;
	for (int i=1;i<=n;i++)
		cin >> a[i];
	for (int i=1;i<=n;i++){
		dis[a[i]]=0;
		if (mark[a[i]]!=-1){
			cout << 0 ;
			return 0;
		}
		mark[a[i]]=a[i];
		Q.push({a[i],0});
	}
	ll ans=1e18+5;
	while (!Q.empty()){
		node U=Q.top();
		Q.pop();
		int u=U.id;
		ll d=U.dis;
		if (ans<d) continue;
		if (d!=dis[u]) continue;
		for (int i=0;i<18;i++){
			int v=u^(1<<i),p=get(u,v,i);
			if (mark[u]!=mark[v]){
				ans=min(ans,dis[u]+dis[v]+p);
			}
			if (dis[u]+p<dis[v]){
				dis[v]=dis[u]+p;
				Q.push({v,dis[v]});
				mark[v]=mark[u];
			}
		}
	}
	cout << ans;
}


C++11(clang++ 3.9) 解法, 执行用时: 3171ms, 内存消耗: 24164K, 提交时间: 2019-04-13 15:48:49

#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<ll,int>PI;
const int N=262150,M=18,K=10000000;
const ll inf=1LL<<60;
int n,i,j,x,vis[N],w[N][M];
ll f[N],ans=inf;
int q[K],h,t;
bool in[N];
inline int po(int a,int b){
  int W=19260817;
  int t=1;
  for(;b;b>>=1,a=1LL*a*a%W)if(b&1)t=1LL*t*a%W;
  return (t%W+W)%W;
}
inline void ext(int x,ll y){
  if(f[x]<=y)return;
  f[x]=y;
  if(in[x])return;
  in[x]=1;
  if(y<f[q[h]])q[--h]=x;else q[++t]=x;
}
int main(){
  scanf("%d",&n);
  for(i=1;i<=n;i++){
    scanf("%d",&x);
    if(vis[x])return puts("0"),0;
    vis[x]=1;
  }
  int m=18;
  n=1<<m;
  for(i=0;i<n;i++)for(j=0;j<m;j++)w[i][j]=po(max(i,i^(1<<j)),1<<j)+1;
  for(i=0;i<m;i++){
    for(j=0;j<n;j++)f[j]=inf,in[j]=0;
    h=K/2;
    t=h-1;
    for(j=0;j<n;j++)if(vis[j])if(j>>i&1)ext(j,0);
    while(h<=t){
      int x=q[h++];
      in[x]=0;
      for(j=0;j<m;j++)ext(x^(1<<j),f[x]+w[x][j]);
    }
    for(j=0;j<n;j++)if((j>>i&1)==0&&vis[j])ans=min(ans,f[j]);
  }
  printf("%lld",ans);
}

上一题