• CSU-ACM2020寒假集训比赛2


    A - Messenger Simulator

    CodeForces - 1288E

    两种解法,我选择了第二种

    mn很好求,联系过就是1,没联系过就是初始位置

    第一种:统计同一个人两次联系之间的出现的不同的人的个数,这个值用来更新mx[i],模拟一下就可以理解。而在第一次联系之前的部分,由于初始是顺序的,所以只需要统计i第一次联系之前出现的大于i的不同的人的个数,用这个数字和初始位置之和来更新mx[i]。对于最后一次联系之后的部分,我们可以假定在m+1的位置再次与其进行联系,然后套用前面两次联系之间的情况即可,需要注意的是对于所有的i都是假设在m+1的位置再次进行联系。对于从来都没有联系过的人,同样假设m+1联系并套用第一次联系之前的模式即可,其实也就是整个序列m中比i大的不同人的个数是多少。复杂度在统计两次联系之间那里,可以用莫队或者主席树实现,其他部分可以用树状数组实现。

    第二种就是模拟这个过程,初始位置对应线段树m+1~m+n区间,每次练习就是把一个人当前位置对应权值-1,然后在最前面+1,容易发现只需要统计每次联系前的位置即可(仍视为m+1次联系了所有的i,其实就是在全部联系结束之后再次统计当前位置)。

     1 #include<bits/stdc++.h>
     2 #define LL long long
     3 #define dl double
     4 using namespace std;
     5 const int inf=3e5+10;
     6 int n,m;
     7 int mn[inf],mx[inf];
     8 int f[inf<<3];
     9 int fir,hs[inf];
    10 #define mid (l+r>>1)
    11 #define rs  (o<<1|1)
    12 #define ls  (o<<1)
    13 void add(int o,int l,int r,int x,int y){
    14  if(l == r){f[o]+=y;return ;}
    15  if(x <= mid)add(ls,l,mid,x,y);
    16  else add(rs,mid+1,r,x,y);
    17  f[o]=f[ls]+f[rs];
    18 }
    19 int query(int o,int l,int r,int x){
    20  if(r <= x)return f[o];
    21  if(x <= mid)return  query(ls,l,mid,x);
    22  else return f[ls]+query(rs,mid+1,r,x);
    23 }
    24 int main(){
    25 // freopen("in.txt","r",stdin);
    26  scanf("%d%d",&n,&m);fir=m;
    27  for(int i=1;i<=n;i++)add(1,1,n+m,hs[i]=i+m,1),mx[i]=mn[i]=i;
    28  for(int i=1;i<=m;i++){
    29   int x;scanf("%d",&x);
    30   mx[x]=max(mx[x],query(1,1,n+m,hs[x]));
    31   add(1,1,n+m,hs[x],-1);
    32   add(1,1,n+m,hs[x]=fir--,1);
    33   mn[x]=1;
    34  }
    35  for(int i=1;i<=n;i++)mx[i]=max(mx[i],query(1,1,n+m,hs[i]));
    36  for(int i=1;i<=n;i++)printf("%d %d
    ",mn[i],mx[i]);
    37  return 0;
    38 }
    View Code

    B - Restore Permutation

    CodeForces - 1208D

    此题很容易想到从后往前处理。最后一个可以直接算出来,然后排除他的影响,算倒数第二个....我们需要证明一个结论,对于当前处理的位置(排除了已经用过的),s[i]严格单调,此结论很容易证明,任取i<j即可发现s[i]<s[j]。我开了一个set储存当前剩余的元素,set内比较的是其在当前位置时对应的s[i],然后lower_bound即可找到答案,nlog^2。

    标答1的解法是:每次寻找s[i]为0的点,最右面的这种点便是1(他的右面不可能有0了),然后把他后面的s[i]都-1,再找2,重复此过程。

    标答2是树状数组做法,同样倒序处理,巧妙运用树状数组免去了二分的麻烦同时省去了"删除操作",也就把我的set直接去掉了。

     1 #include<bits/stdc++.h>
     2 #define LL long long
     3 #define dl double
     4 using namespace std;
     5 const int inf=2e5+10;
     6 int n;
     7 LL s[inf];
     8 LL f[inf];
     9 int a[inf];
    10 int lowbit(int x){return x&(-x);}
    11 LL sum(int x){LL ans=0;for(int i=x;i;i-=lowbit(i))ans+=f[i];return ans;}
    12 void add(int x){for(int i=x;i<=n;i+=lowbit(i))f[i]+=x;}
    13 struct ghb{
    14  int id;
    15  LL val;
    16  bool operator < (const ghb &o)const{
    17   LL tp1=1ll*id*(id-1)/2-sum(id),tp2=1ll*o.id*(o.id-1)/2-sum(o.id);
    18   if(!o.id)tp2=o.val;
    19   return tp1 < tp2;
    20  }
    21 };
    22 set<ghb>S;
    23 set<ghb>::iterator g;
    24 int main(){
    25 // freopen("in.txt","r",stdin);
    26  scanf("%d",&n);
    27  for(int i=1;i<=n;i++)scanf("%lld",&s[i]),S.insert((ghb){i,0});
    28  for(int i=n;i>=1;i--){
    29   g=S.lower_bound((ghb){0,s[i]});
    30   a[i]=(*g).id;add((*g).id);S.erase(g);
    31  }
    32  for(int i=1;i<=n;i++)printf("%d ",a[i]);
    33  return 0;
    34 }
    View Code

    C - Turing Tree

    HDU - 3333

    莫队的模板题

    或者还有两种解法

    第一种:主席树,在线算法,每个位置记录对应数值上一次出现的位置,然后变成了区间查询小于l的元素个数的问题,只不过是把权值1变成了对应数的数值。

    另一种:线段树/树状数组,离线算法,把询问按右节点排序,依次插入每一个数,在树状数组中加上对应值,如果这个数之前出现过,那么把之前的减去对应值。

     1 #include<bits/stdc++.h>
     2 #define LL long long
     3 #define dl double
     4 using namespace std;
     5 const int inf=3e4+10;
     6 int T;
     7 int n,m;
     8 struct ghb{
     9  int id,val1,val2;
    10  bool operator < (const ghb &o)const{return val1 < o.val1;}
    11 }a[inf];
    12 bool cmp1(ghb &a,ghb &b){return a.id < b.id;}
    13 int bel[inf];
    14 struct Query{
    15  int l,r,id;
    16  LL ans;
    17  bool operator < (const Query &o)const{return bel[l] == bel[o.l] ? r<o.r : bel[l]<bel[o.l];}
    18 }q[inf<<2];
    19 bool cmp2(Query &a,Query &b){return a.id < b.id;}
    20 int cnt[inf];
    21 void work(){
    22  LL ans=0;
    23  int l=1,r=0;
    24  for(int i=1;i<=m;i++){
    25   while(l < q[i].l){
    26    cnt[a[l].val2]--;
    27    if(cnt[a[l].val2] == 0)ans-=a[l].val1;
    28    l++;
    29   }
    30   while(l > q[i].l){
    31    l--;
    32    cnt[a[l].val2]++;
    33    if(cnt[a[l].val2] == 1)ans+=a[l].val1;
    34   }
    35   while(r < q[i].r){
    36    r++;
    37    cnt[a[r].val2]++;
    38    if(cnt[a[r].val2] == 1)ans+=a[r].val1;
    39   }
    40   while(r > q[i].r){
    41    cnt[a[r].val2]--;
    42    if(cnt[a[r].val2] == 0)ans-=a[r].val1;
    43    r--;
    44   }
    45   q[i].ans=ans;
    46  }
    47 }
    48 int main(){
    49 // freopen("in.txt","r",stdin);
    50  scanf("%d",&T);
    51  a[0].val1=-1;
    52  while(T--){
    53   memset(cnt,0,sizeof(cnt));
    54   scanf("%d",&n);int len=sqrt(n);
    55   for(int i=1;i<=n;i++)scanf("%d",&a[i].val1),bel[a[i].id=i]=(i-1)/len+1;
    56   sort(a+1,a+n+1);
    57   for(int i=1;i<=n;i++)a[i].val2=a[i].val1 == a[i-1].val1 ? a[i-1].val2 : a[i-1].val2+1;
    58   sort(a+1,a+n+1,cmp1);
    59   scanf("%d",&m);
    60   for(int i=1;i<=m;i++)scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
    61   sort(q+1,q+m+1);
    62   work();
    63   sort(q+1,q+m+1,cmp2);
    64   for(int i=1;i<=m;i++)printf("%lld
    ",q[i].ans);
    65  }
    66  return 0;
    67 }
    68 /*莫队模板题*/
    View Code

    D - Welfare State

    CodeForces - 1199D

    第一感觉就是线段树模拟这个过程,因为只有一次询问所以也不是吉司机那种,然后写的过程中发现有些累赘,感觉有更简单的方法。

    其实可以用O(n)的做法,只要倒序枚举所有操作并记录2操作最大值,在某个位置的1操作第一次被枚举到的时候更新当前点的值即可,同样,从来没被1操作过的可以视为在0处有一个1操作,也就是全枚举完后对没被1操作过的位置取一个max。

    只有线段树的代码

     1 #include<bits/stdc++.h>
     2 #define LL long long
     3 #define dl double
     4 using namespace std;
     5 const int inf=2e5+10;
     6 int n,q;
     7 int f[inf<<2],tag[inf<<2];
     8 #define ls (o<<1)
     9 #define rs (o<<1|1)
    10 #define mid (l+r>>1)
    11 void tagdown(int o){
    12  tag[ls]=max(tag[ls],tag[o]);
    13  tag[rs]=max(tag[rs],tag[o]);
    14  tag[o]=0;
    15 }
    16 void modify(int o,int l,int r,int x,int y){
    17  if(l == r){tag[o]=0;f[o]=y;return ;}
    18  tagdown(o);
    19  if(x <= mid)modify(ls,l,mid,x,y);
    20  else modify(rs,mid+1,r,x,y);
    21 }
    22 void out(int o,int l,int r){
    23  if(l == r){printf("%d ",max(f[o],tag[o]));return ;}
    24  tagdown(o);out(ls,l,mid);out(rs,mid+1,r);
    25 }
    26 int main(){
    27 // freopen("in.txt","r",stdin);
    28  scanf("%d",&n);
    29  for(int i=1;i<=n;i++){int x;scanf("%d",&x);modify(1,1,n,i,x);}
    30  scanf("%d",&q);
    31  for(int i=1;i<=q;i++){
    32   int x,y,z;scanf("%d%d",&x,&y);
    33   if(x == 1){scanf("%d",&z);modify(1,1,n,y,z);}
    34   else tag[1]=max(tag[1],y);
    35  }
    36  out(1,1,n);
    37  return 0;
    38 }
    39 /*又是一道伪吉司机线段树,因为只有一次询问所以很简单*/
    40 /*似乎压根不存在f的转移,应该可以用更简洁的方法做出来*/
    View Code

    E - Playlist

    CodeForces - 1140C

    道理很简单,按b从大到小排序,顺序枚举选择前k大。然而我选择了很愚蠢的二分+树状数组来求前k大,事实上一个优先队列就可以轻松解决。

     1 #include<bits/stdc++.h>
     2 #define LL long long
     3 #define dl double
     4 using namespace std;
     5 const int inf=3e5+10;
     6 int n,k;
     7 struct Song{
     8  int t,b;
     9  bool operator < (const Song &o)const{
    10   return b > o.b;
    11  }
    12 }a[inf];
    13 int lowbit(int x){return x&(-x);}
    14 int f1[inf<<2];
    15 LL f2[inf<<2];
    16 void add(int x){int y=1e6-x+1;for(int i=y;i<=1e6;i+=lowbit(i))f1[i]++,f2[i]+=x;}
    17 int sum1(int x){int ans=0;for(int i=x;i;i-=lowbit(i))ans+=f1[i];return ans;}
    18 LL  sum2(int x){LL  ans=0;for(int i=x;i;i-=lowbit(i))ans+=f2[i];return ans;}
    19 LL ANS;
    20 int main(){
    21 // freopen("in.txt","r",stdin);
    22  scanf("%d%d",&n,&k);
    23  for(int i=1;i<=n;i++)scanf("%d%d",&a[i].t,&a[i].b);
    24  sort(a+1,a+n+1);
    25  for(int i=1;i<=n;i++){
    26   add(a[i].t);
    27   int l1=1,r1=1e6;
    28   while(l1 != r1){
    29    int mid=(l1+r1>>1)+1;
    30    if(sum1(mid) > k)r1=mid-1;
    31    else l1=mid;
    32   }
    33   int l2=1,r2=1e6;
    34   while(l2 != r2){
    35    int mid=l2+r2>>1;
    36    if(sum1(mid) < k)l2=mid+1;
    37    else r2=mid;
    38   }
    39   LL ans=sum2(l1);
    40   int tp1=sum1(l1);
    41   if(tp1 < k && sum1(l2) > k)ans+=1ll*(1e6-l2+1)*(k-tp1);
    42   ANS=max(ANS,ans*a[i].b);
    43  }
    44  printf("%lld
    ",ANS);
    45  return 0;
    46 }
    47 /*二分+树状数组实现前k大?*/
    View Code
  • 相关阅读:
    序列化
    python_模块与包
    python_常用内置模块
    python_生成器
    python_文件操作
    你好,mysql
    2017年12月20日 内置对象
    2017年12月17日 ASP.NET 12个表单元素&&简单控件/复合控件
    2017年12月16日 ASP.NET基本用法
    2017年12月14日 LinQ高级查&&Asp.net WebForm Asp.net MVC
  • 原文地址:https://www.cnblogs.com/hyghb/p/12204776.html
Copyright © 2020-2023  润新知