CF 1041 1042整理

终于回来整理了,这两场比赛我也是醉了,第一场-1分,第二场被skip,还是太菜啊qaq

CF1041

T1T2过水忽略直接看后面

T3大意:给你一个长度为n的序列a1,a2,a3···an,你需要把这些数分成若干组,使得每组数任意两数之差严格大于d,并且满足组数尽可能少。

sol:有理有据的贪心。开一个小根堆,存入每一组的最大的数,当有一个新的数字时,判断它是否大于堆顶的元素+d,若满足就把当前元素归入哪一组并更新小根堆,否则新开一个编号,压入堆中。

#include <queue>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=200005;
struct data{int p,q;}a[N];
inline bool cmp(data a,data b){return a.p<b.p;}
struct node{int num,id;};
priority_queue<node>q;
inline bool operator<(const node &p,const node &q){return p.num>q.num;}
int n,m,d,re=0,b[N];
int main()
{
    int i; scanf("%d%d%d",&n,&m,&d); for(i=1;i<=n;i++)scanf("%d",&a[i].p),a[i].q=i;
    sort(a+1,a+n+1,cmp); re=1; q.push((node){a[1].p,1}); b[a[1].q]=1;
    for(i=2;i<=n;i++)
    {
        node tmp=q.top();
        if(tmp.num+d<a[i].p)
        {
            q.pop(); q.push((node){a[i].p,tmp.id}); b[a[i].q]=tmp.id;
        }
        else
        {
            re++; q.push((node){a[i].p,re}); b[a[i].q]=re;
        }
    }printf("%d
",re); for(i=1;i<n;i++)printf("%d ",b[i]); printf("%d",b[n]);
}
View Code

T4大意:你当前高度为h,你每往前飞一个单位就会下降一个单位(不能贴地飞行),给出n段气流,在气流中你不会下降,求你可以飞的最远距离(不限制起点)ps:气流从左到右有序给出。

sol:先对1~n段气流做一个前缀和,记录1~i段会下降的距离和会向前滑行的距离,易知起点一定是某段气流的左端点(不然开始就白白下降),所以枚举左端点,二分右端点最远可能的位置,得到答案。

#include <cstdio>
#include <algorithm>
using namespace std;
const int N=200005;
#define int long long
int n,h,ans=0,s[N],su[N];
struct node{int x,y;}a[N];
inline bool jud(int S,int T)
{
  if(s[T]-s[S]<h)return true; else return false;
}
signed main()
{
  int i,l,r,mid,tmp; scanf("%lld%lld",&n,&h);    for(i=1;i<=n;i++)scanf("%lld%lld",&a[i].x,&a[i].y),ans=max(ans,a[i].y-a[i].x+h);
  for(i=2;i<=n;i++)s[i]=s[i-1]+a[i].x-a[i-1].y; for(i=1;i<=n;i++)su[i]=su[i-1]+a[i].y-a[i].x;
  for(i=1;i<=n;i++)
  {
    l=i,r=n,mid,tmp;
    while(l<=r)
    {
      mid=(l+r)/2; if(jud(i,mid))l=mid+1,tmp=mid;else r=mid-1;
    }ans=max(ans,su[tmp]-su[i-1]+h);
  }printf("%lld
",ans);
}
View Code

 T5大意:有一棵树,现在给你每条树边被去掉时,形成的两个联通块中点的最大的编号分别是多少,问满足条件的树存不存在,若存在请构造出这棵树

sol:每条边一定有一端的最大值为n,所以我们就围着n建菊花图就好了,左右两端最大值相同的边就串成一条链接在n上

#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int N=1005;
int n,in1[N],a[N],b[N],n1=0,n2=0;
bool G[N][N];
vector<int>V[N];
int main()
{
    int i,j,x,y,bo=0; scanf("%d",&n); memset(in1,0,sizeof in1);
    for(i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y); if(y!=n)return 0*printf("NO
"); in1[x]++;
    }
    for(i=1;i<n;i++)if(!in1[i])a[++n1]=i;else b[++n2]=i;
    for(i=1,j=1;i<=n2;i++)
    {
        if(in1[b[i]]>1)
        {
            in1[b[i]]--;
            while(in1[b[i]]>0&&j<=n1)
            {
                if(b[i]<a[j]){bo=1;break;}
                V[b[i]].push_back(a[j]); in1[b[i]]--; j++;
            }if(bo)break;
        }else G[b[i]][n]=1;
    }
    if(!bo)
    {
        for(i=1;i<n;i++)
        {
            if(V[i].size())
            {
                for(j=0;j<V[i].size()-1;j++)
                {
                    G[V[i][j]][V[i][j+1]]=1;
                }G[i][V[i][0]]=1; G[V[i][V[i].size()-1]][n]=1;
            }
        }printf("YES
");
        for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(G[i][j])printf("%d %d
",i,j);
    }else return 0*printf("NO
");
}
View Code

CF1042

T3大意:给出一个长度为n的序列,每次可以合并两个数ax,ay(ax会消失,ay变成ax*ay),你还有一个机会可以删除一个数,使得最后得到的数最大

sol:大力高精 想都别想 于是乎发现:如果负数有奇数个,那么要删除绝对值最小的那个,如果有0的话,先把0合成一个,以上两样如都有,则合在一起去掉反之则去掉存在的那个,(与此同时要注意,如果只剩一个数,是万万不能删掉的,不要问为什么,全删了答案是什么qaq)

#include <cstdio>
#include <algorithm>
using namespace std;
const int N=200005;
int n,m,a[N],cnt=0,v[N];
int main()
{
    int i,j,po=0,bo=0; scanf("%d",&n); m=n; for(i=1;i<=n;i++)scanf("%d",&a[i]); if(n==1)return 0;
    for(i=1;i<=n;i++)
    {
        if(a[i]<0) {bo++; if(!po)po=i; else if(a[po]<a[i])po=i;}
        else if(a[i]==0)v[++cnt]=i;
    }i=1;
    while(i<cnt)
    {
        printf("1 %d %d
",v[i],v[i+1]); i++; m--; if(m==1) return 0;
    }
    if(bo&1)
    {
        if(cnt)
        {
            printf("1 %d %d
",min(po,v[cnt]),max(po,v[cnt])); m--; if(m!=1)printf("2 %d
",max(po,v[cnt])); else return 0;
        }
        else
        {
            printf("2 %d
",po);
        }j=0;
        for(i=1;i<=n&&j<=n;)
        {
            while((a[i]==0||i==po)&&i<=n)i++; j=i+1; while((a[j]==0||j==po)&&j<=n)j++; if(j>n)return 0;
            printf("1 %d %d
",i,j); i=j; j=i+1; m--; if(m==1)return 0;
        }
    }
    else
    {
        po=0;
        if(cnt)
        {
            printf("2 %d
",v[cnt]);
        }j=0;
        for(i=1;i<=n&&j<=n;)
        {
            while((a[i]==0||i==po)&&i<=n)i++; j=i+1; while((a[j]==0||j==po)&&j<=n)j++; if(j>n)return 0;
            printf("1 %d %d
",i,j); i=j; j=i+1; m--; if(m==1)return 0;
        }
    }
}
View Code

T4大意:给出一个长度为n的序列和一个数d,找出所有连续的子串使得其和小于d,求子串个数

sol:树状数组加二分,先计算前缀和,离散一下,从1~n枚举每个前缀和,统计比s[i]-d大的个数,最后算一下有几个前缀和小于d,加入答案就完了,似乎比T3要水

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
const int N=200005;
int n,m,t,tot=0,s[N],tr[N],pre[N];
struct node{int w,id,nid;}a[N];
inline bool cmp(node a,node b){return a.w<b.w;}
inline bool cmp1(node a,node b){return a.id<b.id;}
#define lowbit(x) ((x)&(-x))
inline void ins(int x){while(x>0){tr[x]++;x-=lowbit(x);}}
inline int que(int x){int re=0;while(x<=tot){re+=tr[x];x+=lowbit(x);}return re;}
inline int ask(int v)
{
    int l=1,r=tot+1,mid,tmp=tot+1;
    while(l<=r)
    {
        mid=(l+r)>>1;if(pre[mid]>=v)r=mid-1,tmp=mid;else l=mid+1;
    }if(pre[tmp]==v)return tmp+1;else return tmp; 
}
signed main()
{
    int i,x,ans=0,po; scanf("%lld%lld",&n,&t);
    for(i=1;i<=n;i++)
    {
        scanf("%lld",&x); s[i]=s[i-1]+x; a[i].w=s[i]; a[i].id=i;
    }sort(a+1,a+n+1,cmp); tot=1; pre[1]=a[1].w; a[1].nid=1;
    for(i=2;i<=n;i++)
    {
        if(a[i].w!=a[i-1].w){tot++;pre[tot]=a[i].w;} a[i].nid=tot;
    }sort(a+1,a+n+1,cmp1);
    for(i=1;i<=n;i++)
    {
        po=ask(pre[a[i].nid]-t); ans+=que(po); ins(a[i].nid);
    }
    for(i=1;i<=n;i++)if(s[i]<t)ans++; printf("%lld
",ans);
}
View Code
原文地址:https://www.cnblogs.com/gaojunonly1/p/9691043.html