牛客NOIP暑期七天营-提高组1

A思路:排个序建立最短路树即可,可以双指针实现。

考察:贪心,构造。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=2000010;
struct in{
    int x,id;
    bool friend operator <(in w,in v){ if(w.x==v.x) return w.id<v.id; return w.x<v.x; }
}s[maxn];
int a[maxn],b[maxn],c[maxn],tot;
int q[maxn],head,tail;
int main()
{
    int N,M;
    scanf("%d%d",&N,&M);
    rep(i,1,N) scanf("%d",&s[i].x),s[i].id=i;
    sort(s+1,s+N+1);
    q[++head]=1;
    rep(i,2,N) {
        while(head>tail&&s[i].x-s[q[tail+1]].x>M) tail++;
        if(head>tail){
            a[i]=s[q[tail+1]].id;
            b[i]=s[i].id;
            c[i]=s[i].x-s[q[tail+1]].x;
        }
        q[++head]=i;
        if(c[i]==0) {
            puts("-1");
            return 0;
        }
    }
    printf("%d\n",N-1);
    rep(i,2,N) printf("%d %d %d\n",a[i],b[i],c[i]);
    return 0;
}

B:显然最大值取决于a[],出现的最高位不同的那一位,假设有最高位的分到A组,不然分到B组,分组后,各取一个x,y,那么ans=min(x^y),这个可以字典树实现。

比较常规。 但是比赛的时候我以为字典树空间不够只能得80分。所以写了分治, 分治复杂度也是O(60*N)的,而且感觉比字典树保险,实现就是每一层,继续分组。

考察:贪心,字典树求最小异或。

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=2000010;
ll a[maxn],b[maxn],c[maxn],ans=0;
void solve(int p,int L1,int R1,int L2,int R2,ll sum)
{if(p==-1){
        ans=min(ans,sum);
        return ;
    }
    int t1=0,t2=0;
    for(int i=L1;i<=R1;i++){
        if(a[i]&(1LL<<p)) b[++t1]=a[i];
        else c[++t2]=a[i];
    }
    rep(i,1,t1) a[L1+i-1]=b[i];
    rep(i,1,t2) a[L1+t1+i-1]=c[i];
 
    int t3=0,t4=0;
    for(int i=L2;i<=R2;i++){
        if(a[i]&(1LL<<p)) b[++t3]=a[i];
        else c[++t4]=a[i];
    }
    rep(i,1,t3) a[L2+i-1]=b[i];
    rep(i,1,t4) a[L2+t3+i-1]=c[i];
    int F=0;
    if(t1&&t3) F=1,solve(p-1,L1,L1+t1-1,L2,L2+t3-1,sum);
    if(t2&&t4) F=1,solve(p-1,L1+t1,R1,L2+t3,R2,sum);
    if(F) return ;
    solve(p-1,L1,R1,L2,R2,sum+(1LL<<p));
}
int main()
{
    int N; scanf("%d",&N);
    rep(i,1,N) scanf("%lld",&a[i]);
    int p=-1;
    for(int i=60;i>=0;i--){
        int tot=0;
        rep(j,1,N) if(a[j]&(1LL<<i)) tot++;
        if(tot!=0&&tot!=N) {
            p=i; break;
        }
    }
    if(p!=-1){
        int t1=0,t2=0; ans=1LL<<61;
        rep(i,1,N) {
            if(a[i]&(1LL<<p)) b[++t1]=a[i];
            else c[++t2]=a[i];
        }
        rep(i,1,t1) a[i]=b[i];
        rep(i,1,t2) a[t1+i]=c[i];
        solve(p-1,1,t1,t1+1,N,1LL<<p);
    }
    printf("%lld\n",ans);
    return 0;
}

C:求最小字典序下的公共路径长度,由于N比较小,显然就是枚举每个点为根,然后最小字典序的  Σ最短路树LCA到根的距离,预料到码量比较长,就写了个floyd骗分,LCA也直接写的倍增(估计写tarjan会快个10倍),60分水过。   字典序那里可以用拓扑排序实现。

考察:最短路,LCA,保证字典序。

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mp make_pair
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int inf=1e9;
const int maxn=2001;
int dis[maxn][maxn];
int Laxt[maxn],Next[6001],To[6001],len[6001],cnt;
int a[6001],b[6001],fa[maxn][12],ans[6001],dep[maxn],N,Q;
bool vis[maxn];
int ind[maxn];
void add(int u,int v,int w)
{
    Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; len[cnt]=w;
}
int LCA(int u,int v)
{
    if(dep[u]<dep[v]) swap(u,v);
    for(int i=11;i>=0;i--) {
        if(dep[fa[u][i]]>=dep[v]) u=fa[u][i];
    }
    if(u==v) return u;
    for(int i=11;i>=0;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
    return fa[u][0];
}
set<pii>s;
void solve(int p)
{
    rep(i,1,N) vis[i]=ind[i]=0;
    rep(i,1,N)
     for(int j=Laxt[i];j;j=Next[j]){
        if(dis[p][i]+len[j]==dis[p][To[j]]) ind[To[j]]++;
     }
    s.insert(mp(p,0));
    while(!s.empty()){
        pii T=*s.begin(); s.erase(s.begin());
        int u=T.first; fa[u][0]=T.second; dep[u]=dep[T.second]+1;
        vis[u]=1;
        for(int i=Laxt[u];i;i=Next[i]) {
            if(dis[p][u]+len[i]==dis[p][To[i]]) {
                ind[To[i]]--;
                if(ind[To[i]]==0) s.insert(mp(To[i],u));
            }
        }
    }
    rep(i,1,11) rep(j,1,N) fa[j][i]=fa[fa[j][i-1]][i-1];
    rep(i,1,Q){
         if(!vis[a[i]]||!vis[b[i]]) continue;
         ans[i]=max(ans[i],dis[p][LCA(a[i],b[i])]);
    }
}
int main()
{
    int M,u,v,w;
    scanf("%d%d%d",&N,&M,&Q);
    rep(i,1,Q) ans[i]=-1;
    rep(i,1,N) rep(j,1,N) dis[i][j]=inf;
    rep(i,1,N) dis[i][i]=0;
    rep(i,1,M) {
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
        dis[u][v]=min(dis[u][v],w);
    }
    rep(i,1,Q) scanf("%d%d",&a[i],&b[i]);
    rep(k,1,N)
     rep(i,1,N)
      if(dis[i][k]!=inf)
        rep(j,1,N)
          dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
    rep(i,1,N) solve(i);
    rep(i,1,Q) printf("%d\n",ans[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/hua-dong/p/11376592.html