zoukankan      html  css  js  c++  java
  • 19_03_26校内训练[魔法卡片]

    题意

    有n张有序的卡片,每张卡片上恰有[1,m]中的每一个数,数字写在正面或反面。每次询问区间[l,r],你可以将卡片上下颠倒,问区间中数字在卡片上方的并的平方和最大是多少。q,n*m≤1,000,000。


    思考

    一个很重要的性质,若区间长度≥log m+1,则答案为12+22+33+...+m2。

    为什么?可以动态地观察。对于没有出现的数字集合,当前卡片上的数字至少有一半的数字出现在正面或者是反面,因此每次你可以将未出现的数字的数量至少减少一半。每次重复上次操作。

    对于长度很小的,我们暴力初始化答案。可以发现,若我们强制不选某一个数字,则一定只有一种方法,即每张卡片有它的均朝下。

    因此最多只有m种方案(可能有重复的)。同时,若区间中的方案数<pow(2,区间长度),必然有方法使得所有数字都至少有一个在上方。


    不需要代码的代码

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long int ll;
     4 const ll maxn=1E6+5;
     5 ll min(ll x,ll y){return x<y?x:y;}
     6 ll n,m,q,x,y,g,f[maxn][20],vis[maxn],ti,sum[maxn],v[maxn],bel[maxn];
     7 vector<int>a[maxn];
     8 int main()
     9 {
    10     ios::sync_with_stdio(false);
    11     cin>>n>>m>>q;
    12     for(int i=1;i<=m;++i)g+=i*i;
    13     for(int i=1;i<=n;++i)
    14     {
    15         cin>>x;
    16         for(int j=0;j<=m;++j)a[i].push_back(0);
    17         for(int j=1;j<=x;++j)
    18         {
    19             cin>>y;
    20             a[i][y]=1;
    21         }
    22     }
    23     for(int l=1;l<=n;++l)
    24     {
    25         for(int i=1;i<=m;++i)bel[i]=0;
    26         for(int r=l;r<=min(n,l+19);++r)
    27         {
    28             ++ti;
    29             if(l!=r&&f[l][r-l]==g){f[l][r-l+1]=g;continue;}
    30             int cur=1<<(r-l+1);
    31             for(int i=1;i<=m;++i)
    32             {
    33                 if(a[r][i])bel[i]|=1<<(r-l);//1或0其实是等价的 
    34                 if(vis[bel[i]]!=ti)
    35                 {
    36                     vis[bel[i]]=ti;
    37                     sum[bel[i]]=0;
    38                     --cur;
    39                 }
    40             }
    41             if(cur>0)f[l][r-l+1]=g;
    42             else
    43             {
    44                 for(int i=1;i<=m;++i)sum[bel[i]]+=i*i;
    45                 for(int i=1;i<=m;++i)f[l][r-l+1]=max(f[l][r-l+1],g-sum[bel[i]]);
    46             }
    47         }
    48     }
    49     while(q--)
    50     {
    51         cin>>x>>y;
    52         if(x>y)swap(x,y);
    53         if(y-x+1>=20)cout<<g<<endl;
    54         else cout<<f[x][y-x+1]<<endl;
    55     }
    56     return 0;
    57 }
    View Code
  • 相关阅读:
    SseEmitter broken pipe
    沉淀vue相关知识(主要还是个人积累用)
    简述箭头函数基本使用和this指向的问题
    前端渲染和后端渲染,前端路由和后端路由
    .net工程师学习vue的心路历程(三)
    .net工程师学习vue的心路历程(一)
    初始mongodb(一)
    .net工程师学习vue的心路历程(二)
    MySQL自我保护参数
    mongodb副本集添加节点
  • 原文地址:https://www.cnblogs.com/GreenDuck/p/10601539.html
Copyright © 2011-2022 走看看