zoukankan      html  css  js  c++  java
  • 【62测试】【状压dp】【dfs序】【线段树】

    第一题:

      给出一个长度不超过100只包含'B'和'R'的字符串,将其无限重复下去。
      比如,BBRB则会形成
      BBRBBBRBBBRB
      现在给出一个区间[l,r]询问该区间内有多少个字符'B'(区间下标从1开始)   [1<=l,r<=1e18]
    解:  没想到第一题这么水。直接前缀和+mod就可以了,再判一下边界。注意1e18不需要高精度。long long 有9*10^18.

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #define maxn 105
     6 #define ll long long
     7 using namespace std;
     8 char c[maxn];
     9 ll len,ans,l,r,sum[maxn];
    10 int main()
    11 {
    12     freopen("a.in","r",stdin);
    13     freopen("a.out","w",stdout);
    14     scanf("%s",c+1);
    15     cin>>l>>r;
    16     len=strlen(c+1);
    17     for (int i=1;i<=len;i++)
    18      {
    19          sum[i]=sum[i-1];
    20          if (c[i]=='B') sum[i]++;
    21      } 
    22     ans=(r/len)*sum[len]+sum[r%len]-(l/len)*sum[len]-sum[l%len];
    23     if ((l%len==0&&c[len]=='B')||c[l%len]=='B') ans++;
    24     printf("%I64d",ans);
    25     return 0;
    26 }

    第二题:(codeforces 580D:Kefa and Dishes)

      我们要从n种食物选m个出来,安排一个顺序吃掉它(们),每种食物有个美味值ai,然后我们有k个规则,每个规则有 xi, yi 和 ci三个数,如果吃完第xi种食物接下来马上吃第yi种食物,第j种食物的美味值会增加ci。每种食物至多吃一个,求美味值最大的和是多少?

      n,m<=18,ai,ci<=10^9

    解:

       看到这么小的数据范围就是状压dp了。想到是第一次自己写,还是很欣慰的得了50%。我的方法是f[i][j]表示选了 i 个的状态 j 。写转移方程的时候遇到了问题,不能表示上一个选的是什么(而从这一点出发可以想到换第一维的表示,可以换为last取的食物,于是我与标答失之交臂)所以把 f 写了一个结构体,存下美味值和上一次选的食物。然后就是转移方程,我先循环m,然后枚举所有状态,判断该状态是否符合只取了当前个食物,然后枚举n,加入下一个状态。

      还是不知道为什么WA了5个点,等写完博客来看看。

      50分代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #define M 300000
     6 #define maxn 20
     7 #define ll long long
     8 using namespace std;
     9 int n,m,k,ma;
    10 ll v[maxn];
    11 struct pp{
    12     ll tastyy;
    13     int chos;    
    14 };
    15 pp f[maxn][M];
    16 ll ad[maxn][maxn];
    17 bool pd(int x,int y)
    18 {
    19     int sum=0;
    20     for (int i=0;i<n;i++)
    21      {
    22          if ((1<<i)&x) sum++; 
    23          if (sum>y-1) return false; 
    24      } 
    25     if (sum==y-1) return true;
    26     else return false;
    27 }
    28 int main()
    29 {
    30     freopen("b.in","r",stdin);
    31     freopen("b.out","w",stdout);
    32     scanf("%d%d%d",&n,&m,&k);
    33     for (int i=1;i<=n;i++)
    34       scanf("%I64d",&v[i]);
    35     for (int i=1;i<=k;i++)
    36     {
    37         int x,y;
    38         ll z;
    39         scanf("%d%d%I64d",&x,&y,&z);
    40         ad[x][y]=z;
    41     }
    42     for (int i=1;i<=m;i++)
    43      {
    44          f[i][0].tastyy=-1;
    45          ma=(ma|(1<<(n-i)));//n-i
    46      } 
    47     for (int i=1;i<=n;i++)
    48       {
    49           int x=(1<<(i-1));// not f[1][i]
    50           f[1][x].tastyy=v[i];
    51           f[1][x].chos=i;
    52       }
    53     for (int i=2;i<=m;i++)
    54     {
    55         for (int j=1;j<=ma;j++)
    56         if (pd(j,i)&&f[i-1][j].tastyy){
    57             for (int k=1;k<=n;k++)
    58             if (f[i][(j&(1<<(k-1)))].tastyy==-1){
    59                 int cur=(j|(1<<(k-1)));//k-1
    60                 ll add=0; 
    61                 if (ad[f[i-1][j].chos][k]) add=ad[f[i-1][j].chos][k];
    62                 if (f[i-1][j].tastyy+v[k]+add>=f[i][cur].tastyy){//放在括号内 
    63                     f[i][cur].tastyy=f[i-1][j].tastyy+v[k]+add;
    64                     f[i][cur].chos=k;
    65                 }
    66             }
    67         }
    68     }
    69     ll ans=0;
    70     for (int i=1;i<=ma;i++)
    71     if (f[m][i].tastyy>=0){
    72         if (f[m][i].tastyy>=ans) ans=f[m][i].tastyy;
    73     }
    74     printf("%I64d",ans);
    75     return 0;
    76 }
    View Code

      说说正解。这道题当初电子科大那位讲过,我说怎么这么眼熟。用f[i][j]表示最后一个选 j 的状态 i 。第一层枚举所有状态,进入后判断是否选了m个更新答案,否则,第二层循环当前要选的点,如果当前状态没有选过,那么进入第三层,循环上一次能选的点,更新状态:f[(i|(1<<(j-1)))][j]=max(f[(i|(1<<(j-1)))][j],f[i][k]+ad[k][j]+v[j]) 。

      AC代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #define M 300000
     6 #define maxn 20
     7 #define ll long long
     8 using namespace std;
     9 int n,m,k;
    10 ll v[maxn],f[M][maxn],ad[maxn][maxn],ans;
    11 bool pd(int x)
    12 {
    13     int sum=0;//sum=0
    14     for (int i=1;i<=n;i++)
    15      if ((1<<(i-1))&x) sum++;//&x
    16     if (sum==m) return true;
    17     else return false; 
    18 }
    19 int main()
    20 {
    21     freopen("b.in","r",stdin);
    22     freopen("b.out","w",stdout);
    23     cin>>n>>m>>k;
    24     for (int i=1;i<=n;i++)
    25       scanf("%I64d",&v[i]);
    26     for (int i=1;i<=k;i++)
    27     {
    28         int x,y;
    29         ll z;
    30         scanf("%d%d%I64d",&x,&y,&z);
    31         ad[x][y]=z;
    32     }
    33     for (int i=0;i<n;i++)
    34       f[(1<<i)][i+1]=v[i+1];
    35     int idx=1;
    36     for (int i=1;i<=(1<<n);i++)
    37     {
    38         if (pd(i)){
    39             for (int j=1;j<=n;j++)
    40               if (f[i][j]>ans) ans=f[i][j];
    41         }
    42         else{
    43             for (int j=1;j<=n;j++)
    44             {
    45                 if (((i>>(j-1))&1)==0)
    46                 {//没有吃 
    47                     for (int k=1;k<=n;k++)
    48                       if ((i>>(k-1))&1)//枚举吃过的最后一个 
    49                       {
    50                           if (f[i][k]+ad[k][j]+v[j]>f[(i|(1<<(j-1)))][j])
    51                           f[(i|(1<<(j-1)))][j]=f[i][k]+ad[k][j]+v[j];
    52                       }        
    53                 }
    54             }
    55         }
    56     }
    57     printf("%I64d",ans);
    58     return 0;
    59 }

    第三题:

      因为外来的入侵,国王决定在某些城市加派士兵。所有城市初始士兵数量为0。当城市i被加派了k名士兵时。城市?的所有子城市需要被加派k + 1名士兵。这些子城市的所有子城市需要被加派k +2名士兵。以此类推。当然,加派士兵的同时,国王也需要不断了解当前的情况。于是他随时可能询问以城市 i 为根的子树中的所有城市共被加派了多少士兵。你现在是国王的军事大臣,你能回答出国王的每个询问么?

      输入格式
    第一行,包含两个整数N,P代表城市数量以及国王的命令的数量。
    第二行N −1个整数,表示2− N号每个节点的父亲节点。
    接下来的P行,每行代表国王的一个命令,命令分两种:
    A X K在城市X加入K个士兵
    Q X 询问以城市X为根的子树中所有士兵数量的和。

      n<=50000,p<=100000

    解:  考试时用20多分钟打的暴力,过了50%。然后这套题就考了200。这是集训以来考的最好的一次,第4吧。

      说正事。这道题的正解:dfs序+线段树。

      dfs序:按照dfs的搜索顺序,一个节点的所有子树都是连续的标号,所以对于这个节点有一个最大区间,可以用线段树进行维护。(QAQ树套树-_-)

      最终还是逃不过线段树。每一次操作u节点对于u的某一子树上的节点v而言,需要加上的值为k+dep[v]-dp[u]. dep[v]为节点本身的性质,而k-dep[u]为子树的性质。所以可以把它分为两部分处理。一个节点如果被操作了tot次,那么第一部分为tot*dep[v] , 对于子树而言,为tot*∑dep[v]。同样第二部分,每个子节点都是k-dep[u],所以对于这棵子树为size*(k-dep[u]).

      然后就是线段树的长长代码.....还要多练啊。提速。注意一些细节,毕竟代码长了容易犯小错误,而且这样的小错是很难找到的。我运气还算比较好。

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<algorithm>
      5 #define maxn 50005
      6 #define ll long long 
      7 #define L(x) (x<<1)
      8 #define R(x) ((x<<1)|1)
      9 #ifdef WIN32
     10 #define AUTO "%I64d"
     11 #else 
     12 #define AUTO "%lld"
     13 #endif
     14 using namespace std;
     15 int n,q,ans,idx;
     16 int tot,ne[maxn*2],to[maxn*2],he[maxn],id[maxn]; 
     17 int xul[maxn],xur[maxn],depth[maxn];
     18 
     19 void add(int a,int b)
     20 {
     21     tot++;to[tot]=b;ne[tot]=he[a];he[a]=tot;
     22 }
     23 void dfs_xu(int x,int fa,int dep)
     24 {
     25     depth[x]=dep;
     26     xul[x]=++idx;//正序左 
     27     id[idx]=x;//反之 
     28     for (int i=he[x];i;i=ne[i])
     29     if (to[i]!=fa){
     30         dfs_xu(to[i],x,dep+1);
     31     }
     32     xur[x]=idx;//右端点 
     33 }
     34 /* /*-----------------xian_duan_shu-----------------------*/ 
     35 struct pp{
     36     ll dep,sum_tot,tot;//sum_tot: part_1
     37     ll cha,sum_cha,size;//sum_cha作为子树的sum,cha 作为单点的cha, 
     38 };
     39 pp tree[maxn*4];
     40 /*-------------add_dan----------------*/
     41 void add_tot(int x,ll val)
     42 {
     43     tree[x].tot+=val;
     44     tree[x].sum_tot+=val*tree[x].dep;
     45 }
     46 void add_cha(int x,ll val)
     47 {
     48     tree[x].cha+=val;
     49     tree[x].sum_cha+=val*tree[x].size;
     50 }
     51 /*-------------------pushdown--------------------*/
     52 void pushdown_tot(int cur)
     53 {
     54     if (!tree[cur].tot) return ;
     55     add_tot(L(cur),tree[cur].tot);
     56     add_tot(R(cur),tree[cur].tot);
     57     tree[cur].tot=0;
     58 }
     59 void pushdown_cha(int cur)
     60 {
     61     if(!tree[cur].cha) return ;
     62     add_cha(L(cur),tree[cur].cha);
     63     add_cha(R(cur),tree[cur].cha);
     64     tree[cur].cha=0;
     65 }
     66 /*----------------------uptate_sum---------------------*/
     67 void uptate_tot(int cur)
     68 {
     69     tree[cur].sum_tot=tree[L(cur)].sum_tot+tree[R(cur)].sum_tot;
     70 }
     71 void uptate_cha(int cur)
     72 {
     73     tree[cur].sum_cha=tree[L(cur)].sum_cha+tree[R(cur)].sum_cha;
     74 }
     75 /*-----------------------------opreate-------------------------------*/
     76 void change_tot(int cur,int l,int r,int x,int y,ll val)
     77 {
     78     if (l>=x&&r<=y)
     79     {
     80         add_tot(cur,val);
     81         return ;
     82     }
     83     pushdown_tot(cur);
     84     int mid=(l+r)>>1;
     85     if (x<=mid&&l<=y) change_tot(L(cur),l,mid,x,y,val);
     86     if (y>=mid+1&&x<=r) change_tot(R(cur),mid+1,r,x,y,val);
     87     uptate_tot(cur);
     88 }
     89 void change_cha(int cur,int l,int r,int x,int y,ll val)
     90 {
     91     if (l>=x&&r<=y)
     92     {
     93         add_cha(cur,val);
     94         return ;
     95     }
     96     pushdown_cha(cur);
     97     int mid=(l+r)>>1;
     98     if (x<=mid&&l<=y) change_cha(L(cur),l,mid,x,y,val);
     99     if (y>=mid+1&&x<=r) change_cha(R(cur),mid+1,r,x,y,val);
    100     uptate_cha(cur);
    101 }
    102 ll query(int cur,int l,int r,int x,int y)
    103 {
    104     if (x<=l&&r<=y)
    105         return tree[cur].sum_tot+tree[cur].sum_cha;
    106     pushdown_tot(cur);pushdown_cha(cur);
    107     int mid=(l+r)>>1;
    108     ll an=0;
    109     if (x<=mid&&l<=y) an+=query(L(cur),l,mid,x,y);
    110     if (y>=mid+1&&x<=r) an+=query(R(cur),mid+1,r,x,y);
    111     return an;
    112 }
    113 /*----------------------build-----------------------*/
    114 
    115 void update_build(int u)
    116 {
    117     tree[u].size=tree[L(u)].size+tree[R(u)].size;
    118     tree[u].dep=tree[L(u)].dep+tree[R(u)].dep;//细节 
    119 }
    120 void build(int l,int r,int u)
    121 {
    122     if (l==r)
    123     {
    124         tree[u].dep=depth[id[l]];
    125         tree[u].size=1;
    126         return ;//****
    127     }
    128     int mid=(l+r)>>1;
    129     build(l,mid,L(u));
    130     build(mid+1,r,R(u));//mid+1
    131     update_build(u);
    132 }
    133 /*-------------------------main----------------------------*/
    134 int main()
    135 {
    136     freopen("c.in","r",stdin);
    137     freopen("c.out","w",stdout);
    138     cin>>n>>q;
    139     for (int i=2;i<=n;i++)
    140     {
    141         int x;
    142         scanf("%d",&x);
    143         add(x,i);
    144         add(i,x);
    145     }
    146     dfs_xu(1,0,1);
    147     build(1,n,1);
    148     while (q)
    149     {
    150         char c[2];int x;
    151         scanf("%s",c);
    152         if (c[0]=='A') {
    153             int x;
    154             ll y;
    155             scanf("%d"AUTO,&x,&y);
    156             change_tot(1,1,n,xul[x],xur[x],1);
    157             change_cha(1,1,n,xul[x],xur[x],y-depth[x]);
    158         }
    159         else {
    160             int x;
    161             scanf("%d",&x);
    162             ll ans=query(1,1,n,xul[x],xur[x]);
    163             printf(AUTO,ans);
    164             printf("
    ");
    165         }
    166         q--;
    167     }
    168     return 0;
    169 }

     

       

      

  • 相关阅读:
    【数据结构】并查集
    项目管理【12】 | 项目范围管理-收集需求
    项目管理【11】 | 项目范围管理-规划范围管理
    项目管理【10】 | 项目范围管理-范围管理概述
    Visual Studio代码远程调试方法
    项目管理【09】 | 项目整体管理-结束项目或阶段
    操作系统【2】Linux系统安装
    操作系统【1】Linux基础知识
    移动端开发案例【2】头部组件开发
    移动端开发案例【1】全局样式配置
  • 原文地址:https://www.cnblogs.com/lx0319/p/6052284.html
Copyright © 2011-2022 走看看