二中模拟赛 9.11

第一题 permutation

是一道爱斯基摩人的逆序对题(形容它不裸的程度)

如果从上往下看,会发现横线的作用是交换当前它左右两侧的数。因此这道题的第一部分可以直接从上往下暴力swap。

对于第二部分,把这样一张图倒过来看,可以发现倒过来的图也就是把一个排列经过m次交换恢复顺序。

而最少的横线数,也就是最少的交换次数,就是逆序对数。

第一部分模拟交换+第二部分归并排序统计逆序对数 100分

#include<cstdio>
using namespace std;
const int N=1000007;
int n,m,hx[N],a[N],c[N],cnt;
void sort(int l,int r)
{
    if(l==r) return;
    int mid=l+r>>1;int t=l,q=mid+1,p=l-1;
    sort(l,mid);sort(mid+1,r);
    while(q<=r&&t<=mid)
    {
        if(a[t]>a[q])
        {
            cnt+=mid-t+1;
            c[++p]=a[q];q++;
        }
        else c[++p]=a[t++];
    }
    while(q<=r) c[++p]=a[q++];
    while(t<=mid)c[++p]=a[t++];
    for(int i=l;i<=r;i++) a[i]=c[i];
}
void swap(int &x,int &y)
{
    int temp=x;x=y;y=temp;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) scanf("%d",&hx[i]);
    for(int i=1;i<=n;i++) a[i]=i; 
    for(int i=1;i<=m;i++) swap(a[hx[i]],a[hx[i]+1]);
    for(int i=1;i<=n;i++) printf("%d ",a[i]);
    sort(1,n);
//-        for(int i=1;i<=n;i++) printf("%d ",a[i]);
    printf("\n%d",cnt);
}

考试的时候以为是道图论题,在那方面想了半天没想出来就放弃了。还是太年轻。

 

第二题 mst最小生成树

它真的是个最小生成树!!从来没有见过这么耿直的题目名称-_-

对包含第k条边的最小生成树而言,若k在这张图的最小生成树上,则答案为最小生成树的权值。

若k不在最小生成树上,考虑在最小生成树上加上k这条边,此时我们会发现,原来有n-1条边,现在有n条边,显然出现了一个环。

显然在这个环上删去任意一条边,都不会影响全图的点互相连通。所以在环上选出除边k之外最长的一条边删掉即可。

而这个问题可以转化为在最小生成树上,求两点之间路径上边权最大的边的边权。

考虑倍增。

Kruskal+倍增 100分

注意预处理边权时,为了覆盖到x到f[x][i]的所有路径,需要在e[x][i-1]和e[fa[x][i-1]][i-1]之间取max。因为没有注意到这点而改了一晚上-_-

#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int N=400007;
int n,m,fa[N][21],head[N],head2[N],f[N],ed[N][21],dep[N],etot,e2tot;
long long anst;
bool vis[N];
struct node{
    int from,next,to,v,num;
}e[N<<1],e2[N<<1];
void adde(int x,int y,int v,int num)
{
    e[++etot].to=y;
    e[etot].from=x;
    e[etot].next=head[x];
    e[etot].v=v;
    e[etot].num=num;
    head[x]=etot;
}
void adde2(int x,int y,int v)
{
    e2[++e2tot].to=y;
    e2[e2tot].next=head2[x];
    e2[e2tot].v=v;
    head2[x]=e2tot;
}
int find(int x)
{
    if(f[x]!=x) f[x]=find(f[x]); return f[x];
}
void unionn(int x,int y)
{
    f[find(x)]=y;
}
void kruskal()
{
    for(int i=1;i<=m;i++)
    {                        
        if(find(e[i].from)==find(e[i].to))     continue;
        unionn(e[i].from,e[i].to);
        vis[e[i].num]=1;anst+=e[i].v;
        adde2(e[i].from,e[i].to,e[i].v);
        adde2(e[i].to,e[i].from,e[i].v);
    }
}
void getf(int x,int f,int v)
{
    fa[x][0]=f;ed[x][0]=v;
    dep[x]=dep[f]+1;
    for(int i=1;i<=17;i++)
    {
        fa[x][i]=fa[fa[x][i-1]][i-1];
        ed[x][i]=max(ed[x][i-1],ed[fa[x][i-1]][i-1]);
    }
    for(int i=head2[x];i;i=e2[i].next)
    {
        if(e2[i].to==f) continue;
        getf(e2[i].to,x,e2[i].v);
    }
}
int lca(int x,int y)
{
    int s=0;
    if(dep[y]>dep[x]) swap(x,y);        
    for(int i=20;i>=0;i--)
    {
        if(dep[fa[x][i]]>=dep[y])
        {
            s=max(s,ed[x][i]);
            x=fa[x][i];
        }
    }
    if(x==y) return s;
    for(int i=20;i>=0;i--)
    {
        if(fa[x][i]!=fa[y][i])
        {
            s=max(s,max(ed[x][i],ed[y][i]));
            x=fa[x][i];y=fa[y][i];
        }
    }
    s=max(s,max(ed[x][0],ed[y][0]));
    
    return s;
}
bool comp(node x,node y)
{
    return x.v<y.v;
}
bool comp2(node x,node y)
{
    return x.num<y.num;
}
int main()
{
    scanf("%d%d",&n,&m);
    int x,y,v;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&v);
        adde(x,y,v,i);
    }
    for(int i=1;i<=n;i++) f[i]=i;
    sort(e+1,e+1+m,comp);
    kruskal();
    sort(e+1,e+1+m,comp2);
    getf(1,0,0);
    for(int i=1;i<=m;i++)
    {
        if(vis[i]) printf("%lld\n",anst);
        else printf("%lld\n",anst-lca(e[i].from,e[i].to)+e[i].v);
    }
    return 0;
}

一点闲话:写的50分暴力因为数组开小了只得了20分;考试的时候想到了正解,但是写到一半的时候竟然因为突然觉得很不靠谱而放弃了:)

 

转载于:https://www.cnblogs.com/IvyLH03/p/11517409.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值