day7上

t1
在桌面上放着n张纸牌,每张纸牌有两面,每面都写着一个非负整数。你的邪王真眼可以看到所有牌朝上的一面和朝下的一面写的数字。现在你需要将一些牌翻过来,使得所有牌朝上的一面中,至少有一半(≥n/2)的数字是一样的。请你求出最少需要翻几张牌,或者判断无解。
注意:在翻牌的时候,你不能把牌扔掉,不能偷偷把别的牌放进来,也不能用笔涂改牌上面的数字。

离散化,在贪心,找最多的向上的,个数大于n/2的

#include<cstdio>
#include<algorithm>
using namespace std;
int n,b[110000],a[110000],tmp[119999],tot,cnt[110000],cnt2[110000],maxn;
int main(){
    freopen("card.in","r",stdin);
    freopen("card.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        scanf("%d",&a[i]),tmp[++tot]=a[i];
        scanf("%d",&b[i]),tmp[++tot]=b[i];

    }
    sort(tmp+1,tmp+tot+1);
    int t1=unique(tmp+1,tmp+tot+1)-tmp-1;
    //for(int i=1;i<=t1;i++) printf("%d ",tmp[i]);
    for(int i=1;i<=n;i++)
    a[i]=lower_bound(tmp,tmp+t1+1,a[i])-tmp-1,b[i]=lower_bound(tmp+1,tmp+t1+1,b[i])-tmp-1;
    for(int i=1;i<=n;i++){
        //printf("%d ",a[i]); 
        cnt[a[i]]++;cnt2[a[i]]++;
        cnt[b[i]]+=(a[i]==b[i])?0:1;
    }
    sort(a+1,a+n+1);
    t1=unique(a+1,a+n+1)-a-1;
    for(int i=1;i<=t1;i++)
    if(cnt[a[i]]>n/2&&cnt2[a[i]]>maxn) maxn=cnt2[a[i]];
    //printf("%d ",maxn);
    if(maxn<(n+1)/2&&maxn)
    printf("%d",(n+1)/2-maxn);
    else if(maxn>(n+1)/2) printf("0");
    else printf("Impossible");
}

t2
给定一个字符串S,它的长为n,后缀数组的功能是,将其所有后缀按字典序从小到大排好序。我们对其做一点小小的改动:再给定一个数字m,记ssi表示从S的第i位开始、长度最多为m的子串,我们希望将这些字符串{ssi}按字典序从小到大排序。举个栗子,当S=”abcab”,m=2时,ssi的值分别为:
ss1=”ab”
ss2=”bc”
ss3=”ca”
ss4=”ab”
ss5=”b”
但是,只是把{ssi}全部排好序还是太简单了。初始状态下,ss1~ssn按顺序排成一行,我们只能通过不断交换某两个相邻字符串的位置来做排序。再举个栗子,把上面提到的ss1~ss5排好序的一种方案是:
(0)原序列:”ab”, “bc”, “ca”, “ab”, “b”
(1)交换第3和第4个串:”ab”, “bc”, “ab”, ca”, “b”
(2)交换第2和第3个串:”ab”, “ab”, “bc”, ca”, “b”
(3)交换第4和第5个串:”ab”, “ab”, “bc”, b”, “ca”
(4)交换第3和第4个串:”ab”, “ab”, “b”, bc”, “ca”
现在,你需要求出,最少通过多少次相邻字符串交换,才能把所有子串{ssi}排成字典序从小到大的形式。

归并排序,把比数字大小改成比字典序,用哈希维护,二分第一个不行的

#include <cstdio>
#include<iostream>
#define p 1000000007
#define N 50055
long long n,m,hash[1100000],pow[1100000],a[1100000],f[1100000],tmp[1100000],ans;

bool comp(int l,int r){
    if(l==r) return 1;
    int L=0,R=m;
    if (n-r+2<R) R=n-r+2;
    if (n-l+2<R) R=n-l+2;
    while(L<=R)
    {
        int mid=(L+R)>>1;
        long long has=(hash[l+mid-1]-1ll*hash[l-1]*pow[mid]%p+p)%p;
        long long has2=(hash[r+mid-1]-1ll*hash[r-1]*pow[mid]%p+p)%p;
        if(has==has2) L=mid+1;
        else R=mid-1;
    }
    if(L==m) return 1;
    return a[l+L-1]<=a[r+L-1];
}
void sort(int l,int r){
    if(l==r) return ;
    int mid=(l+r)>>1;
    sort(l,mid);
    sort(mid+1,r);
    int t1=l,t2=mid+1,flag;
    for(int i=l;i<=r;i++){
        if(t1>mid) flag=0;
        else if(t2>r) flag=1;
        else flag=comp(f[t1],f[t2]);
        if(flag)
            tmp[i]=f[t1++];
        else tmp[i]=f[t2++],ans+=mid-t1+1;
    }
    for(int i=l;i<=r;i++)
    f[i]=tmp[i];
    return ;
}
int main(){
    freopen("sort.in","r",stdin);
    freopen("sort.out","w",stdout);
    scanf("%d%d",&n,&m);
    pow[0]=1;
    for(int i=1;i<=n;i++){
        char x;
        std::cin>>x;
        a[i]=x-96;
        hash[i]=(1ll*hash[i-1]*29+a[i])%p;
        pow[i]=pow[i-1]*29ll%p;
        f[i]=i;
    }
    a[n+1]=0;
    sort(1,n);
    printf("%lld",ans);
} 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值