省队集训Round3 DAY1

T1

这里写图片描述
这里写图片描述

题解

大概的思路就是对于每个位置的数,如果他前面比他大的数的个数>=k,那么将他向前移动k位,否则扔到一个数组中。最后对这个数组从小到大排序,然后将数组中的数插入序列中的空位置。
为什么这样做是对的,对于前面比他大的数个数大于等于k的数来说,他们是没有机会自己移动的,只可能是他前面比他大的前k个数向后移而使他向前移动。对于小于k的数来说,首先他前面比他大的数会使他向前移动,其次他自己会向后移动。对于剩下位置不能直接计算的数来说一定是有序的,因为他停下来一定是遇到了一个比他大的数或者不能移动,如果他后面有比他小的数那么他一定会向后移动。那么有没有可能x-1,x,x-2,其中x的位置是直接计算的。x-1一定是停在了第k个比他大的数的前面(k包括一开始在他前面比他大的数以及移动过程中遇到的比他大的数),那么上述位置中相当于x-2前面有k+1个数比他大,这种数的位置是可以直接计算的。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 200003
using namespace std;
int ans[N],a[N],b[N],c[N],root[N],n,k,top,st[N],sz,cnt,vis[N];
struct data{
    int ls,rs,sum;
}tr[N*60];
void insert(int &i,int l,int r,int x)
{
    tr[++sz]=tr[i]; i=sz;
    tr[i].sum++;
    if (l==r) return;
    int mid=(l+r)/2;
    if (x<=mid) insert(tr[i].ls,l,mid,x);
    else insert(tr[i].rs,mid+1,r,x);
}
int query(int now,int l,int r,int ll,int rr)
{
    if (ll>rr) return 0;
    if (ll<=l&&r<=rr) return tr[now].sum;
    int mid=(l+r)/2; int ans=0;
    if (ll<=mid) ans+=query(tr[now].ls,l,mid,ll,rr);
    if (rr>mid) ans+=query(tr[now].rs,mid+1,r,ll,rr);
    return ans;
}
int main()
{
    freopen("fable.in","r",stdin);
    freopen("fable.out","w",stdout);
    scanf("%d%d",&n,&k);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
    sort(b+1,b+n+1);
    cnt=unique(b+1,b+n+1)-b-1;
    for (int i=1;i<=n;i++) c[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
    for (int i=1;i<=n;i++) 
     root[i]=root[i-1],insert(root[i],1,cnt,c[i]);
    for (int i=1;i<=n;i++) {
        int t=query(root[i-1],1,cnt,c[i]+1,cnt);
        if (t>=k) ans[i-k]=a[i],vis[i-k]=1;
        else st[++top]=a[i];
    }
    sort(st+1,st+top+1);
    int now=1;
    for (int i=1;i<=n;i++)
     if (!vis[i]) {
        ans[i]=st[now];
        now++;
     }
    for (int i=1;i<=n;i++) printf("%d\n",ans[i]);
}


T2

这里写图片描述
这里写图片描述

题解

可并堆。
首先说明一下矩阵的行列式。
这里写图片描述其中 s g n(σ)是排列σ的符号差
通俗一点说,就是如果我们从一个n*n的矩阵中选择出n个点,是每行每列都只有一个数别选中,那么选择的方案有n!种,正好对应了1..n的所有排列。对于一个排列如果逆序对数为奇数那么sgn为-1,否则为1.后面的乘积就是选中位置的数的乘积。
对于这道题来说,每个数有一个可以选取的范围 [li,ri] ,如果我们令这些位置的值为1,剩下的位置的值为0,那么求矩阵的行列式其实就可以看出满足 pi[li,ri] 是逆序对为奇数的多还是偶数的多,因为1的乘积都是1,所以会相互抵消。
求行列式比较常见的方式就是高斯消元,但是那是 O(n3) ,因为这个矩阵的每行都是一个区间,所以考虑用这个性质优化。
对于每一列只保留r最小的区间,剩下l相同的区间消掉最小的区间后,与r+1列合并。这个过程用可并堆来实现。
消完后,对于每列只有一个区间的端点在上面,因为我们要求第i行的区间的左端点必须是第i列,所以需要进行交换,每交换一次行列式的值乘(-1)。最后如果行列式的值为1,那么输出Y,如果行列式的值为-1,那么输出F。如果某列没有区间那么行列式的值为0,输出D。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 100003
using namespace std;
struct data{
    int l,r,h;
}a[N],b[N];
int fa[N],lch[N],rch[N],d[N],root[N],pd[N],n,pos[N],mp[N];
int cmp(data a,data b){
    return a.l<b.l;
}
int find(int x)
{
    if (fa[x]==x) return x;
    fa[x]=find(fa[x]);
    return fa[x];
}
int merge(int x,int y)
{
    if (!x) return y;
    if (!y) return x;
    if (a[x].r>a[y].r) swap(x,y);
    rch[x]=merge(rch[x],y);
    if (d[lch[x]]<d[rch[x]]) swap(lch[x],rch[x]);
    d[x]=d[rch[x]]+1;
    return x;
}
int main()
{
     freopen("fiend.in","r",stdin);
     freopen("fiend.out","w",stdout);
     int T; scanf("%d",&T);
     while (T--) {
        scanf("%d",&n);
        memset(pd,0,sizeof(pd));
        memset(lch,0,sizeof(lch));
        memset(rch,0,sizeof(rch));
        memset(d,0,sizeof(d));
        memset(fa,0,sizeof(fa));
        for (int i=1;i<=n;i++){
            scanf("%d%d",&a[i].l,&a[i].r);
            a[i].h=i; pd[a[i].l]=1;
        }
        for (int i=1;i<=n;i++) fa[i]=i;
        sort(a+1,a+n+1,cmp);
        memset(root,0,sizeof(root));
        for (int i=1;i<=n;i++) {
         int t=0;
         if (a[i].l==a[i-1].l) {
            int r1=find(i); int r2=find(i-1);
            t=merge(r1,r2);
            fa[r2]=fa[r1]=t;
         }
         if (a[i].l!=a[i+1].l||i==n) 
           if (t) root[a[i].l]=t;
           else root[a[i].l]=i;
         }
        bool mark=false;
        for (int i=1;i<=n;i++) {
            if (root[i]) b[i]=a[root[i]];
            else {
                mark=true;
                break;
            }
            int t=merge(lch[root[i]],rch[root[i]]);
            fa[lch[root[i]]]=fa[rch[root[i]]]=t;
            while (a[t].r==b[i].r) 
              t=merge(lch[t],rch[t]),fa[lch[t]]=fa[rch[t]]=t;
            int t1=merge(root[b[i].r+1],t);
            fa[root[b[i].r+1]]=fa[t]=t1;
            root[b[i].r+1]=t1;
        }
        if (mark) { 
            printf("D\n");
            continue;
        }
        int ans=1;
        for (int i=1;i<=n;i++) pos[i]=b[i].h,mp[b[i].h]=i;
        for (int i=1;i<=n;i++)
         if (pos[i]!=i) {
            ans=ans*(-1);
            pos[mp[i]]=pos[i];
            mp[pos[i]]=mp[i];
         }
        if (ans==-1) printf("F\n");
        else printf("Y\n");
     }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值