• 2020.11.30~2020.12.6 做题记录


    P1552 [APIO2012]派遣(线段树合并+二分)

    线段树合并板子题,维护子树内薪水为i的忍者有多少个,并记录薪水的和。为了使能被派遣的忍者更多,贪心取子树内尽可能多的便宜忍者,线段树上二分即可

    直接线段树合并可能会卡空间,离散化一下

    线段树合并的最后一层不能pushup!!!会变成0的,因此需要边合并边求和

      1 #include <cmath>
      2 #include <cstdio>
      3 #include <cstring>
      4 #include <algorithm>
      5 #define N1 100005
      6 #define M1 N1*50
      7 #define ll long long
      8 using namespace std;
      9 
     10 int n,S,tot,maxn;
     11 int real[N1];
     12 struct node{
     13     int id,val;
     14     bool operator < (const node &s2) const
     15     {
     16         if(val!=s2.val) return val<s2.val;
     17         return id<s2.id;
     18     }
     19 }a[N1];
     20 
     21 struct Edge{
     22 int to[N1],nxt[N1],head[N1],cte;
     23 void ae(int u,int v)
     24 {
     25     cte++; to[cte]=v, nxt[cte]=head[u];
     26     head[u]=cte;
     27 }
     28 }e;
     29 struct SEGTREE{
     30 int ls[M1],rs[M1],sum[M1],root[N1],cnt;
     31 ll cost[M1];
     32 void pushup(int rt)
     33 {
     34     sum[rt]=sum[ls[rt]]+sum[rs[rt]];
     35     cost[rt]=cost[ls[rt]]+cost[rs[rt]];
     36 }
     37 void upd(int x,int l,int r,int &rt,int w)
     38 {
     39     if(!rt) rt=++cnt;
     40     sum[rt]+=w, cost[rt]+=real[x]; 
     41     if(l==r) return; 
     42     // if(l==r) { sum[rt]+=w, cost[rt]+=x; return; }
     43     int mid=(l+r)>>1;
     44     if(x<=mid) upd(x,l,mid,ls[rt],w);
     45     else upd(x,mid+1,r,rs[rt],w);
     46     pushup(rt);
     47 }
     48 int mrg(int r1,int r2)
     49 {
     50     if(!r1||!r2) return r1+r2;
     51     int rt=++cnt;
     52     sum[rt]=sum[r1]+sum[r2];
     53     cost[rt]=cost[r1]+cost[r2];
     54     ls[rt]=mrg(ls[r1],ls[r2]);
     55     rs[rt]=mrg(rs[r1],rs[r2]);
     56     // pushup(rt);
     57     return rt;
     58 }
     59 ll query(ll res,int l,int r,int rt)
     60 {
     61     if(res>=cost[rt]) return sum[rt];
     62     if(l==r) return 0;
     63     int mid=(l+r)>>1;
     64     if(res>=cost[ls[rt]]) return sum[ls[rt]]+query(res-cost[ls[rt]],mid+1,r,rs[rt]);
     65     else return query(res,l,mid,ls[rt]);
     66 }
     67 }s;
     68 
     69 int fa[N1],sal[N1],lea[N1];
     70 
     71 ll dfs(int u)
     72 {
     73     int j,v; ll ans=0;
     74     s.upd(sal[u],1,maxn,s.root[u],1);
     75     for(j=e.head[u];j;j=e.nxt[j])
     76     {
     77         v=e.to[j]; if(v==fa[u]) continue;
     78         ans=max(ans,dfs(v));
     79         s.root[u]=s.mrg(s.root[u],s.root[v]);
     80      }
     81      ans=max(ans,1ll*lea[u]*s.query(tot,1,maxn,s.root[u]));
     82      return ans;
     83 }
     84 int main() 
     85 {
     86 //    freopen("a.txt","r",stdin);
     87     scanf("%d%d",&n,&tot);
     88     for(int i=1;i<=n;i++)
     89     {
     90         scanf("%d%d%d",&fa[i],&sal[i],&lea[i]);
     91         if(fa[i]) e.ae(fa[i],i);
     92         else S=i;
     93         a[i]=(node){i,sal[i]}; 
     94     }
     95     sort(a+1,a+n+1);
     96     maxn=1;
     97     for(int i=1;i<=n;i++)
     98     {
     99         if(a[i].val!=a[i-1].val) maxn++, real[maxn]=a[i].val;
    100         sal[a[i].id]=maxn;
    101     }
    102     ll ans=dfs(S);
    103     printf("%lld
    ",ans);
    104     return 0;
    105 }
    View Code

     

    #273 DNA序列 (DP)

    设计$DP$,$f[i][j]$表示第一个串匹配到第i位,第二个串匹配到j位时的最大匹配值

    然而连续空格的负贡献会减少,容易想到分三种情况讨论,所以加一维记录

    $f[i][j][0/1/2]$分别表示两个串分别匹配到i,j时,最后一个位置是 x x // x _ // _ x 时的答案 (x是字母,_是空格)

    转移方程很水,注意得赋初始值,但还不能直接memset,会T..

     1 #include <cmath>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 #define N1 3040
     6 #define inf 0x3f3f3f3f
     7 #define ll long long
     8 using namespace std;
     9 
    10 char str1[N1],str2[N1];
    11 int a[N1],b[N1],w[4][4],f[N1][N1][3];
    12 int n,m,A,B;
    13 int idx(char c)
    14 {
    15     if(c=='A') return 0;
    16     if(c=='T') return 1;
    17     if(c=='G') return 2;
    18     if(c=='C') return 3;
    19     return -1;
    20 }
    21 
    22 int main() 
    23 {
    24 //    freopen("a.txt","r",stdin);
    25     int i,j,k;
    26     scanf("%s",str1+1); n=strlen(str1+1);
    27     for(i=1;i<=n;i++) a[i]=idx(str1[i]);
    28     scanf("%s",str2+1); m=strlen(str2+1);
    29     for(i=1;i<=m;i++) b[i]=idx(str2[i]);
    30     for(i=0;i<4;i++) for(j=0;j<4;j++) scanf("%d",&w[i][j]);
    31     scanf("%d%d",&A,&B);
    32     //memset(f,-0x3f,sizeof(f));
    33     f[0][0][0]=0; f[0][0][1]=f[0][0][2]=-inf;
    34     for(i=0;i<=n;i++) 
    35     {
    36         for(j=0;j<=m;j++)
    37         {
    38             if(!i&&!j) continue;
    39             f[i][j][0]=f[i][j][1]=f[i][j][2]=-inf;
    40             if(i>0 && j>0)
    41             for(k=0;k<=2;k++) //x x
    42                 f[i][j][0]=max(f[i][j][0], f[i-1][j-1][k]+w[a[i]][b[j]] );
    43 
    44             //x _
    45             if(i>0)
    46             {
    47                 f[i][j][1]=max(f[i][j][1], f[i-1][j][0]-A );
    48                 f[i][j][1]=max(f[i][j][1], f[i-1][j][1]-B );
    49                 f[i][j][1]=max(f[i][j][1], f[i-1][j][2]-A );
    50             }
    51 
    52             //_ x
    53             if(j>0)
    54             {
    55                 f[i][j][2]=max(f[i][j][2], f[i][j-1][0]-A );
    56                 f[i][j][2]=max(f[i][j][2], f[i][j-1][1]-A );
    57                 f[i][j][2]=max(f[i][j][2], f[i][j-1][2]-B );
    58             }
    59         }
    60     }
    61     int ans=-inf;
    62     ans=max(ans,f[n][m][0]); ans=max(ans,f[n][m][1]); ans=max(ans,f[n][m][2]);
    63     printf("%d
    ",ans);
    64     return 0;
    65 }
    View Code

    发现如果只是经停某个点的话,在这个点$DP$状态会很难设计。

    实际上我们只考虑$DP$游览的位置就行了,中间走哪条路?当然是最短路

    $n$比较小,$floyd$即可处理出任意两点间最短路长度

    定义$f[i][t]$表示游览第i个点,经过的总时长为t时的最大满意度

    容易得到$f[j][t+dis(i,j)+cost[j]]=min(f[i][t])+satisfy[j]$,且$sat[j]>sat[i]$

    只能由满意度小的位置转移到满意度大的位置,所以需要根据满意度的值来确定枚举顺序

     1 #include <cmath>
     2 #include <queue>
     3 #include <cstdio>
     4 #include <cstring>
     5 #include <algorithm>
     6 #define N1 105
     7 #define T1 305
     8 #define M1 10050
     9 #define inf 0x3f3f3f3f
    10 #define ll long long
    11 using namespace std;
    12 
    13 int n,m,S,T,E;
    14 struct Edge{
    15 int to[M1],nxt[M1],len[M1],cte; int head[N1];
    16 void ae(int u,int v,int w)
    17 {
    18     cte++; to[cte]=v; nxt[cte]=head[u];
    19     head[u]=cte; len[cte]=w;
    20 }
    21 }e;
    22 
    23 int dis[N1][N1],used[N1],cost[N1],sat[N1];
    24 int f[N1][T1],id[N1];
    25 int cmp(int x,int y){ return sat[x]<sat[y]; }
    26 void floyd()
    27 {
    28     int i,j,k;
    29     for(i=0;i<n;i++) dis[i][i]=0;
    30     for(k=0;k<n;k++)
    31     {
    32         for(i=0;i<n;i++)
    33         {
    34             for(j=0;j<n;j++)
    35             {
    36                 if(i==j) continue;
    37                 dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
    38             }
    39         }
    40     }
    41 }
    42 
    43 
    44 int main() 
    45 {
    46     freopen("a.txt","r",stdin);
    47     int i,j,k,x,y,w,ans;
    48     scanf("%d%d%d%d%d",&n,&m,&T,&S,&E);
    49     for(i=0;i<n;i++) scanf("%d",&cost[i]);
    50     for(i=0;i<n;i++) scanf("%d",&sat[i]);
    51     memset(dis,0x3f,sizeof(dis));
    52     for(i=1;i<=m;i++)
    53     {
    54         scanf("%d%d%d",&x,&y,&w);
    55         e.ae(x,y,w), e.ae(y,x,w);
    56         dis[x][y]=min(dis[x][y],w); dis[y][x]=min(dis[y][x],w); 
    57     }
    58     floyd();
    59     for(i=0;i<n;i++) id[i]=i; 
    60     sort(id,id+n,cmp);
    61     memset(f,-0x3f,sizeof(f));
    62     for(i=0;i<n;i++) if(dis[S][i]+cost[i]<=T) f[i][dis[S][i]+cost[i]]=sat[i]; //S->i
    63     for(x=0;x<n;x++) //i->j
    64     {
    65         i=id[x];
    66         for(j=0;j<n;j++) if(sat[j]>sat[i])
    67         {
    68             for(k=0;k+dis[i][j]+cost[j]<=T;k++)
    69             {
    70                 f[j][k+dis[i][j]+cost[j]]=max(f[j][k+dis[i][j]+cost[j]], f[i][k]+sat[j]);
    71             }
    72         }
    73     }
    74     ans=0;
    75     for(j=0;j<n;j++) //j->T
    76     {
    77         for(k=0;k+dis[j][E]<=T;k++)
    78         {
    79             ans=max(f[j][k],ans);
    80         }
    81     }
    82     printf("%d
    ",ans);
    83     return 0;
    84 }
    View Code

    神仙计数,想不出qwq,只能看题解了

    题意:一行$n$个数,初始时全部为0。 需要进行$K$次操作,第$i$次操作为指定一个非空区间$[Li,Ri]$,将这个区间中的数全部+1。需要保证$K$次操作后每个数都不大于$T$。 求有多少种选择区间的方案。 两种方案不同当且仅当存在一个操作,在两种方案中指定的区间不同。$n,K,Tle 40$

    考虑把统计一种方案,转换成从左向右扫描每个位置。扫到左端点=添加区间。扫到右端点=结束区间。

    定义$f[i][j][k]$表示当前枚举到第$i$位,有$j$个区间还未结束,总计添加了$k$个区间的方案数。容易发现,我们还要枚举在第$i$个位置有$end$个区间结束,在第$i+1$个位置有sta个区间开始。得到转移方程:

    $f[i+1][j-end+sta][k+sta]+=f[i][j][i]C_{j}^{end}C_{k+sta}^{sta}$

    $C_{k+sta}^{sta}$表示把新加的$sta$个区间和原来的$k$个区间排列,由于新加的$sta$个区间是相同的(目前来看),所以用组合数而不用排列数

    $C_{j}^{end}$表示从$j$个未结束区间中选择$end$个结束掉,在添加区间的过程中已经乘过排列区间的贡献,所以用组合数

    时间复杂度$O(nK^{4})$

    仔细想想,这种计数方法能规避多次操作相同区间造成的重复方案,同时保证相同起点/终点的区间贡献都是正确的

     1 #include <cmath>
     2 #include <queue>
     3 #include <cstdio>
     4 #include <cstring>
     5 #include <algorithm>
     6 #define N1 42
     7 #define inf 0x3f3f3f3f
     8 #define ll long long
     9 #define p 1011110011
    10 using namespace std;
    11 
    12 int n,K,T;
    13 ll f[N1][N1][N1],mul[N1],inv[N1],minv[N1];
    14 ll C(int x,int y){ if(y>x) return 0; return mul[x]*minv[y]%p*minv[x-y]%p; }
    15 void pre()
    16 {
    17     mul[0]=mul[1]=inv[0]=inv[1]=minv[0]=minv[1]=1;
    18     for(int i=2;i<=K;i++) inv[i]=1ll*(p-p/i)*inv[p%i]%p;
    19     for(int i=2;i<=K;i++) mul[i]=mul[i-1]*i%p, minv[i]=minv[i-1]*inv[i]%p;
    20     for(int j=0;j<=T;j++) f[0][j][j]=1; //pre
    21 }
    22 
    23 int main() 
    24 {
    25 //    freopen("a.txt","r",stdin);
    26     scanf("%d%d%d",&n,&K,&T);
    27     int i,j,k,end,sta;
    28     pre();
    29     for(i=0;i<n;i++)
    30     for(j=0;j<=T;j++) //remain
    31     for(k=0;k<=K;k++) //ends
    32     {
    33         for(end=0;end<=j;end++)
    34         for(sta=0;k+sta<=K;sta++)
    35         {
    36             (f[i+1][j-end+sta][k+sta]+=f[i][j][k]*C(j,end)%p*C(k+sta,sta)%p)%=p;
    37         }
    38     }
    39     printf("%d
    ",f[n][0][K]);
    40     return 0;
    41 }
    View Code

    P6932 [ICPC2017 WF]Money for Nothing(决策单调+分治)

    原题解

    首先考虑贪心,进货尽可能时间早,出货尽可能时间晚。

    以时间为x轴,价格为y轴。按时间排序并筛除那些不优秀的点之后,容易发现进货和出货都是单调递减的离散函数。——感性理解一下,进货出货都一样,时间更早的价格更高,时间更晚的价格更低,才能赚的最多

    推一下决策单调,假设当前进货商a的最佳出货商是b,那么a+1的决策位置一定是b或b右面的商家

    最优决策下,每个进货点都对应某个或某些的出货点,放到坐标系里发现是按顺序排列的点对

    怎么利用这个性质?分治!

    找出mid的决策位置t,那么[l,mid-1]和[mid+1,r]的决策位置一定在t两侧,递归处理即可

     1 #include <cmath>
     2 #include <queue>
     3 #include <cstdio>
     4 #include <cstring>
     5 #include <algorithm>
     6 #define N1 500005
     7 #define inf 0x3f3f3f3f
     8 #define ll long long
     9 using namespace std;
    10 
    11 const ll linf=1e18;
    12 struct node{
    13 int x,y;
    14 }su[N1],de[N1];
    15 int cmp1(node a,node b)
    16 {
    17     if(a.x!=b.x) return a.x<b.x; 
    18     return a.y>b.y;
    19 }
    20 int cmp2(node a,node b)
    21 {   
    22     if(a.x!=b.x) return a.x<b.x; 
    23     return a.y<b.y;
    24 }
    25 
    26 int m,n,mm,nn;
    27 int d[N1],p[N1],e[N1],q[N1];
    28 void del()
    29 {
    30     int i,j,mi; 
    31     for(i=1,mi=inf;i<=m;i++)
    32     {
    33         if(su[i].x==su[i-1].x || su[i].y>=mi) continue;
    34         mm++; d[mm]=su[i].x, p[mm]=su[i].y, mi=su[i].y;
    35     }
    36     for(i=n,mi=0;i>=1;i--)
    37     {
    38         if(de[i].x==de[i+1].x || de[i].y<=mi) continue;
    39         nn++; e[nn]=de[i].x, q[nn]=de[i].y, mi=de[i].y;
    40     }
    41     for(i=1;i<=nn;i++) if(i<nn-i+1) 
    42         swap(e[i],e[nn-i+1]), swap(q[i],q[nn-i+1]);
    43 }
    44 ll query(int i,int j)
    45 { 
    46     if(e[j]<=d[i]) return -linf; 
    47     return 1ll*(e[j]-d[i])*(q[j]-p[i]); 
    48 }
    49 ll solve(int l1,int r1,int l2,int r2)
    50 {
    51     int mid=(l1+r1)>>1,i,st=-1,ed=-1; ll ma=-linf,ans=0,tmp;
    52     for(i=l2;i<=r2;i++)
    53     {
    54         tmp=query(mid,i);
    55         if(tmp>ma) ma=tmp,st=ed=i;
    56         else if(tmp==ma) ed=i;
    57     }
    58     ans=max(ans,ma);
    59     if(l1<mid) tmp=solve(l1,mid-1,l2,st), ans=max(ans,tmp);
    60     if(mid<r1) tmp=solve(mid+1,r1,ed,r2), ans=max(ans,tmp);
    61     return ans;
    62 }
    63 
    64 int main() 
    65 {
    66 //    freopen("a.in","r",stdin);
    67     scanf("%d%d",&m,&n);
    68     for(int i=1;i<=m;i++) scanf("%d%d",&su[i].y,&su[i].x);
    69     for(int i=1;i<=n;i++) scanf("%d%d",&de[i].y,&de[i].x);
    70     sort(su+1,su+m+1,cmp2); sort(de+1,de+n+1,cmp2);
    71     del();
    72     ll ans=solve(1,mm,1,nn);
    73     for(int i=1;i<=mm;i++) if(d[i]==3 && p[i]==5)
    74         n=n;
    75     printf("%lld
    ",ans);
    76     return 0;
    77 }
    View Code

    CF1453E Dog Snacks(树上构造)

    比赛的时候思路对了,但时间不够,就没敲 

    下考和dkr交流,发现似乎是水题。今天改题敲30min就过了

    定义$f[x]$表示狗狗吃掉$x$子树内最后一个零食时距离$x$的距离最小值

    有这样一个贪心结论:

    对于每个节点,狗狗离开$x$子树的时候一定从x子树内“最浅的地方”离开,能使得所需的$k$最小。这个“最浅的地方”有多浅是个根据$x$相邻的子节点的子树情况而定的,并不一定是距离$x$最近的末端节点(可以归纳想象一下)

    那么有$f[x]=min(f[v]+1)$,$f[x]=0(x:endpoint)$

    而节点$x$子树的答案则是$max(f[v]+2)$,不仅要从$v$跑到$x$,还需要进入x另一个子树的头,所以+2

    注意1号节点答案可能有区别,$f[v]$最深的位置我们可以最后走,这样回来的时候跑到1就行了。容易发现这种情况和最大的f[v]数量有关,如果是一个,刚才的策略成立,如果是两个或更多,就不可行了,总得有一个v还需要进入另一个子树头

     1 #include <cmath>
     2 #include <queue>
     3 #include <cstdio>
     4 #include <cstring>
     5 #include <algorithm>
     6 #define N1 200010
     7 #define M1 2*N1
     8 #define inf 0x3f3f3f3f
     9 #define ll long long
    10 using namespace std;
    11 
    12 const ll linf=1e18;
    13 struct Edge{
    14 int to[M1],nxt[M1],head[N1],cte;
    15 void ae(int u,int v)
    16 {
    17     cte++; to[cte]=v, nxt[cte]=head[u];
    18     head[u]=cte;
    19 }
    20 void clr(int n)
    21 {
    22     for(int i=1;i<=n;i++) head[i]=0;
    23     for(int i=1;i<=cte;i++) to[cte]=nxt[cte]=0;
    24     cte=0;
    25 }
    26 }e;
    27 
    28 int T;
    29 int n;
    30 int sha[N1],mi[N1];
    31 void dfs(int u,int fa)
    32 {
    33     int j,v;
    34     sha[u]=mi[u]=0;
    35     for(j=e.head[u];j;j=e.nxt[j])
    36     {
    37         v=e.to[j]; if(v==fa) continue;
    38         dfs(v,u);
    39         if(!sha[u]) sha[u]=inf;
    40         sha[u]=min(sha[u],sha[v]+1);
    41         mi[u]=max(mi[u],sha[v]+1);
    42     }
    43     mi[u]++; //折回来一下,末端点可忽略
    44     if(u==1){
    45         int cnt=0; mi[u]=0;
    46         for(j=e.head[u];j;j=e.nxt[j])
    47         {
    48             v=e.to[j]; 
    49             if(sha[v]+1>mi[u]) mi[u]=sha[v]+1, cnt=1;
    50             else if(sha[v]+1==mi[u]) cnt++;
    51         }
    52         if(cnt>1) mi[u]++;
    53     }
    54 }
    55 
    56 int main() 
    57 {
    58     scanf("%d",&T);
    59     while(T--)
    60     {
    61         scanf("%d",&n);
    62         int x,y,ans=0;
    63         for(int i=1;i<n;i++) 
    64         {
    65             scanf("%d%d",&x,&y);
    66             e.ae(x,y), e.ae(y,x);
    67         }
    68         dfs(1,-1);
    69         for(int i=1;i<=n;i++) ans=max(ans,mi[i]);
    70         printf("%d
    ",ans);
    71         e.clr(n);
    72     }
    73     return 0;
    74 }
    View Code
  • 相关阅读:
    js获取当前页面url网址等信息
    jQuery 复选框全选/取消全选/反选
    js获取日期实例之昨天今天和明天、后天
    mysql不重启修改参数变量
    php 读取功能分割大文件实例详解
    php批量删除数据库下指定前缀的表
    PHP遍历目录返回统计目录大小实例
    PHP二维数组如何根据某个字段排序
    PHP 如何获取二维数组中某个key的集合(高性能查找)
    jQuery 隐藏与显示 input 默认值
  • 原文地址:https://www.cnblogs.com/guapisolo/p/14078292.html
Copyright © 2020-2023  润新知