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

题目传送门

题意:给n个数,给定一个转换规则跟计算方法,找两两转化的最小值。

看完大佬的代码我就来出题解了!!!

由于这个东西我感觉好难,我还是写详细的题解吧。。。。

这是题目给的fairy的定义。那么x^(2^(i))这个东西肯定要预处理出来,方便后面的爆搜emmm

那么这个东西的预处理公式是:x^2^(i)=x^(2*2^(i-1))=x^(2^(i-1))*x^(2^(i-1))

这个东西就是要我们找在给出的那些值中,两两相互转换的最小值多大。。。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define P pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
const int N=1<<18;
const int mod=19260817;
void read(int &a)
{
    a=0;
    int d=1;
    char ch;
    while(ch=getchar(),ch>'9'||ch<'0')
        if(ch=='-')
            d=-1;
    a=ch-'0';
    while(ch=getchar(),ch>='0'&&ch<='9')
        a=a*10+ch-'0';
    a*=d;
}
void write(int x)
{
    if(x<0)
        putchar(45),x=-x;
    if(x>9)
        write(x/10);
    putchar(x%10+'0');
}
struct note
{
    int id;
    int v;
    bool operator < (const note &x) const
    {
        return x.v<v;
    }
};
int vis[N],a[N],f[N][20],dis[N];///f数组为i^(2^(j)),vis[i]=j表示i从j转换而来,dis[i]表示到达i这个点的最小转化值
bool mark[N],book[N];///book标记出队
vector <P> s[N];
priority_queue <note> q;
int main()
{
    int n;
    ll ans=0x3fffffff;
    int x;
    read(n);
    bool flag=false;
    for(re int i=0;i<n;i++)
    {
        read(a[i]);
        if(mark[a[i]])
            flag=1;
        mark[a[i]]=1;
    }
    if(flag)
        return puts("0"),0;
    for(re int i=0;i<N;i++)
    {
        f[i][0]=i;
        for(re int j=1;j<18;j++)
            f[i][j]=1ll*f[i][j-1]*f[i][j-1]%mod;///预处理
    }
    for(re int i=0;i<N;i++)
    {
        for(re int j=0;j<18;j++)
        {
            x=max(i,i^(1<<j));
            s[i].pb(mp(i^(1<<j),f[x][j]+1));///记录所有的fairy(i,j)
        }
        if(mark[i])
        {
            vis[i]=i;
            dis[i]=0;
            q.push({i,0});
        }
        else
        {
            vis[i]=-1;
            dis[i]=0x3fffffff;
        }
    }
    note t;
    while(!q.empty())///爆搜emmm
    {
        t=q.top();
        q.pop();
        if(t.v>=ans)///优先队列按小的排,如果已经大于ans了,那么可以退出了。
            break;
        if(book[t.id])
            continue;
        book[t.id]=1;
        for(re int i=0;i<s[t.id].size();i++)
        {
            int j=s[t.id][i].fi;
            int k=s[t.id][i].se;
            if(vis[j]!=-1&&vis[j]!=vis[t.id])///vis[j]!=vis[t.id]的原因是如果相等,那么这个点是由t.id拓展来的,再算多一次就重复计算了
                ans=min(ans,1ll*(dis[j]+k+t.v));///dis[j]表示跑来j这个点的花费
            if(dis[j]<=k+t.v)
                continue;
            dis[j]=k+t.v;
            vis[j]=vis[t.id];///更新这个点的来源
            q.push({j,dis[j]});///更新j的值并加入队列
        }
    }
    printf("%lld",ans%mod);
    return 0;
}

原文地址:https://www.cnblogs.com/acm1ruoji/p/10714120.html