洛谷4月月赛R1

T1.题目大意:n个人站成一排,有m个团队,每个人有且属于一个团队,可以让若干个人出队,任意交换这些人的位置后再站回去,问要让所有同一团队的人连续地站在一起,至少要出队几个。(n<=10^5,m<=20)

思路:求至少出队多少个等同于求至多有几个人可以不改变位置。假设我们确定了最后每个团队前面都站了哪些团队,显然每一队所在的区间都是确定的,考虑状压DP,f[i]表示状态为i(状态中表示已经加入了哪些团队)时最小出队人数,枚举一个团队j加入状态,对每个团队做前缀和即可知道该团队在一个区间里有多少人,即可转移,时间复杂度O(n*m+m*2^m)。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MM 20
#define MN 100000
int b[MN+5],a[MM+5],s[MM+1][MN+5],f[1<<MM],p[1<<MM];
int main()
{
    int n,m,i,j;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;++i)scanf("%d",&b[i]),++a[b[i]],s[b[i]][i]=1;
    for(i=1;i<=m;++i)for(j=1;j<=n;++j)s[i][j]+=s[i][j-1];
    memset(f,127,sizeof(f));f[0]=0;
    for(i=0;i<1<<m;++i)
    {
        for(j=1;j<=m;++j)if(~i&(1<<j-1))
            f[i|(1<<j-1)]=min(f[i|(1<<j-1)],f[i]+a[j]-s[j][p[i]+a[j]]+s[j][p[i]]),
            p[i|(1<<j-1)]=p[i]+a[j];
    }
    printf("%d",f[(1<<m)-1]);
}

 

T2.题目大意:n个车站编号1~n,有三种车,慢车,特急车,快车,慢车走一站要时间a,特急要b,快车要c,慢车可以在任意站停,特急车只能在给定的m个车站停,现在要定k个快车站,其中必须覆盖所有特急车站,快车只能在快车站停,问从1号站出发,至多有几个站能在t时间内到达(下车、转车耗时0)。(n<=10^9,m<=k<=3000,b<c<a)

思路:先计算出不设快车站(或者说先设m个快车站覆盖特急车站)时能到达哪些站,显然要到这些站是先做特急车然后再坐一段慢车,于是我们可以算出从每个特急站下车坐慢车能到的最远的站(但要在下一个特急站之前,为了不重复统计),现在如果要新建一个快车站,自然我们应该建在某个从特急站下车后坐慢车能到的最远的站的下一个站,这样我们从特急站下车后就能直接搭上快车坐到这个站再坐慢车,我们先对每个候选的站都先算出建了这个站后能新到多少站,扔到堆里,每次我们取出最大的建,建了这个站后我们得到一种新的选择,就是在这个快车站下车后坐慢车能到的站的后一个站再建一个快车站,我们重新算一遍再扔回堆里即可。总复杂度O(klogk),实际上不用堆打个暴力找最大也能过嘛。

#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
#define ll long long
#define MN 3000
#define mp(x,y) make_pair(x,y)
int a,b,c,s[MN+5],r[MN+5];ll t;
priority_queue<pair<int,int> > pq;
void push(int x)
{
    ll v=t-(ll)s[x]*b-(ll)(r[x]-s[x])*c;
    if(v<0){pq.push(mp(0,x));return;}
    int cp=r[x];
    r[x]=min(r[x]+v/a+1,(ll)s[x+1]);
    pq.push(mp(r[x]-cp,x));
}
int main()
{
    int n,m,k,i,ans=0;
    scanf("%d%d%d%d%d%d%lld",&n,&m,&k,&a,&b,&c,&t);
    for(i=1;i<=m;++i)scanf("%d",&s[i]),--s[i];s[i]=n;
    for(i=1;i<=m;++i)
    {
        if((ll)s[i]*b>t)break;
        r[i]=min(s[i]+(t-(ll)s[i]*b)/a+1,(ll)s[i+1]);
        ans+=r[i]-s[i];
        push(i);
    }
    for(i=k-m;i--;)
    {
        ans+=pq.top().first;
        push(pq.top().second);
        pq.pop();
    }
    printf("%d",ans-1);
}

 

T3.没有这题

 

T4.题目大意:某派对搞了两个n个团队的人气排行榜,排行榜上有两个值,分别为排名第几的团队的所属学校和人气值,但第二个榜的报道出了点偏差,有几个学校写错了,有两个条件:一个团队的学校不会改变;由于第二个榜在第一个榜之后出,同一团队在第二个榜的人气不会低于在第一个榜的人气。现在问至少改几个第二个榜的学校才能让信息合法。(n<=200,000)

思路:我们让第一个榜和第二个榜的团队一一对应来建立二分图匹配模型,第一个榜的一个排名和第二个榜的一个排名能匹配当且仅当第二个榜的这个排名的人气不小于第一个榜的人气,且若这两个排名的学校相同,花费为0,否则花费为1,然后我们就能建出一个边数为O(n^2)的费用流图,大概能拿30分。把这个图优化到O(n)就能拿70分,优化思路是这样的:把第二个榜的每个点拆成两个点,分别表示与相同学校的匹配和任意学校匹配,然后再建一个点限制这两个点的流量,之后第一个榜的每个点分别向能匹配到的最小的任意学校点连费用1,向能匹配到的最小的相同学校连费用0,第二个榜的所有点再从小往大连流量INF费用0,图就建完了。但我们显然还是过不了200,000。其实这个匹配过程不用建图优化,是可以直接贪心解决的,每次找到最小的第二个榜的点向前匹配,如果我们找到了与它相同学校的点,显然直接匹配最优(因为此时不大于它的所有点向后能连的边是相同的),如果没有找到,我们只好选一个不同学校的匹配,但我们一时无法抉择选出哪个,就先记下在这之后要为它留下一个点匹配,我们用线段树维护每个未能直接匹配到的第二个榜的点,不大于它的点至多还能取几个,每次要找匹配点时,找到最大的不大于它的第一个榜的点,计算区间最小值看看会不会与前面冲突,不冲突就匹配并区间-1。总复杂度O(nlogn)。

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
inline int read()
{
    int x;char c;
    while((c=getchar())<'0'||c>'9');
    for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
    return x;
}
#define MN 200000
#define INF 0x7FFFFFFF
#define L (k<<1)
#define R (k<<1|1)
struct team{int t,p,x;}p[MN*2+5];
bool cmp(team a,team b){return a.p==b.p?a.x>b.x:a.p<b.p;}
vector<int> q[MN+5];
struct node{int l,r,mn,mk;}t[MN*4+5];
int b[MN+5],bn;
inline void up(int k){t[k].mn=min(t[L].mn,t[R].mn);}
inline void mark(int k,int x){t[k].mn+=x;t[k].mk+=x;}
inline void down(int k){t[k].mk?(mark(L,t[k].mk),mark(R,t[k].mk),t[k].mk=0):0;}
void build(int k,int l,int r)
{
    t[k].mn=INF;
    if((t[k].l=l)==(t[k].r=r))return;
    int mid=l+r>>1;
    build(L,l,mid);build(R,mid+1,r);
}
int query(int k,int l,int r)
{
    if(t[k].l==l&&t[k].r==r)return t[k].mn;
    down(k);
    int mid=t[k].l+t[k].r>>1;
    if(r<=mid)return query(L,l,r);
    if(l>mid)return query(R,l,r);
    return min(query(L,l,mid),query(R,mid+1,r));
}
void change(int k,int x,int z)
{
    if(t[k].l==t[k].r){t[k].mn=z;return;}
    down(k);
    change(x>t[k].l+t[k].r>>1?R:L,x,z);
    up(k);
}
void dec(int k,int l,int r)
{
    if(t[k].l==l&&t[k].r==r){mark(k,-1);return;}
    down(k);
    int mid=t[k].l+t[k].r>>1;
    if(r<=mid)dec(L,l,r);
    else if(l>mid)dec(R,l,r);
    else dec(L,l,mid),dec(R,mid+1,r);
    up(k);
}
int main()
{
    int n=read(),i,s=0,x,ans=0;
    for(i=0;i<n;++i)p[i].t=read(),p[i].p=read(),p[i].x=1;
    for(i=0;i<n;++i)p[i+n].t=read(),p[i+n].p=read();
    build(1,1,n);n<<=1;sort(p,p+n,cmp);
    for(i=0;i<n;++i)
        if(p[i].x)q[p[i].t].push_back(p[i].p),++s;
        else
            if(!q[p[i].t].empty())
            {
                x=lower_bound(b+1,b+bn+1,q[p[i].t][q[p[i].t].size()-1])-b;
                if(x>bn||query(1,x,bn))q[p[i].t].pop_back(),--s,x<=bn?(dec(1,x,bn),0):0;
                else b[++bn]=p[i].p,change(1,bn,s-bn),++ans;
            }
            else b[++bn]=p[i].p,change(1,bn,s-bn),++ans;
    printf("%d",ans);
}

 

转载于:https://www.cnblogs.com/ditoly/p/Luogu-2017-4-R1.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值