ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

ABC 265

2022-08-22 13:03:00  阅读:154  来源: 互联网

标签:le int text ll cin ABC 265 dp


E - Warp(计数、枚举、DP)

Problem

在一个二维平面上,你从原点开始,可以移动\(N\)次,每次可以进行下面三种移动,假设当前位置是\((x,y)\)

  • \((x,y)\rightarrow (x+A,y+B)\)
  • \((x,y)\rightarrow (x+C,y+D)\)
  • \((x,y)\rightarrow (x+E,y+F)\)

不过平面上还有\(M\)个障碍,不能移动到障碍上面。求进行\(N\)次移动移动之后,可以形成多少种不同的路径,答案对\(998244353\)取模

\(1\le N\le 300\),\(1\le M\le 10^5\),\(-10^9\le A,B,C,D,E,F\le 10^9\)

Solve

  • hit1:\(n\)很小
  • hit2:类比从一个点只走右、上到另一个点的方案数的求法

定义\(dp[i][j][k]\)表示当前进行了\(i\)次操作\(1\),\(j\)次操作\(2\),\(k\)次操作\(3\)可以得到的路径条数。不过由于一个点可能通过相同的\(i,j,k\)得到,但由于执行顺序不同会导致路径不同,所以采用刷表法来转移\(dp\)

Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=998244353;
int dx[4],dy[4];
ll dp[305][305][305];
int main(){
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  int n,m;
  cin>>n>>m;
  cin>>dx[0]>>dy[0]>>dx[1]>>dy[1]>>dx[2]>>dy[2];
  map<pair<ll,ll>,bool>ob;
  for(int i=0;i<m;i++){
     int x,y;
     cin>>x>>y;
     ob[{x,y}]=1;
  }
  dp[0][0][0]=1;
  ll ans=0;
  for(int i=0;i<=n;i++)
    for(int j=0;j<=n;j++)
      for(int k=0;k<=n;k++){
         if(i+j+k>n) break;
         ll xx=i*dx[0]+j*dx[1]+k*dx[2];
         ll yy=i*dy[0]+j*dy[1]+k*dy[2];
         if(ob.find({xx,yy})!=ob.end()) continue;
         if(i+j+k==n) ans=(ans+dp[i][j][k])%mod;
         else{
             dp[i+1][j][k]=(dp[i+1][j][k]+dp[i][j][k])%mod;
             dp[i][j+1][k]=(dp[i][j+1][k]+dp[i][j][k])%mod;
             dp[i][j][k+1]=(dp[i][j][k+1]+dp[i][j][k])%mod;
         }
      }
  cout<<ans<<'\n';
}

F.Manhattan Cafe(DP优化)

Problem

定义两个长度为\(N\)的序列\(X、Y\)之间的距离为\(\text{dis}(X,Y)=\sum_{i+0}^N|X_i-Y_i|\)。现在给定两个长度为\(N\)的序列\(p,q\)和一个整数\(D\),问有多少个长度为\(N\)的序列\(r\)满足\(\text{dis}(q,r)\le D\)并且\(\text{}(p,r)\le D\)

\(1\le N\le 100\),\(1\le D\le 1000\),\(-1000\le p_i,q_i\le 1000\)

Solve

定义\(dp[t][i][j]\)表示\(\sum_{k=1}^t|p_k-r_k|=i\)并且\(\sum_{k=1}^t|q_k-r_k|=j\)的方案数。那么转移就可以写成这样

f1[1001][1001],f2[1001][1001];
f1[0][0]=1;
for(int k=1;k<=n;k++){
  memset(f2,0,sizeof f2);
  int x=p[k],y=q[k];
  for(int rt=-2000;rt<=2000;rt++){ //枚举rt
    int di=abs(x-rt);
    int dj=abs(y-rt);
    if(min(di,dj)>D) continue;
    for(int i=0;i<=D-di;i++) //枚举dp[t-1][i][j]的i,j
      for(int j=0;j<=D-dj;j++)
        f2[i+di][j+dj]+=f1[i][j];
  }
  f1=f2;
}

但问题是这样的时间复杂度是\(O(ND^3)\),明显会超时。考虑优化,如果记\(s=|p_t-q_t|\),那么所有\((d_i,d_j)\)的组合可以分成\(3\)类

  • \((s,0),(s-1,1),\cdots,(0,s)\)
  • \((s+1,1),(s+2,2),(s+3,3),\cdots\)
  • \((1,s+1),(2,s+2),(3,s+3),\cdots\)

对于每种情况单独用\(O(D^2)\)转移,这样总的时间复杂度就是\(O(ND^2)\)。

Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=998244353;
int main(){
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  int n,d;
  cin>>n>>d;
  vector<int>q(n),p(n);
  for(auto &x:p) cin>>x;
  for(auto &x:q) cin>>x;
  vector<vector<ll>>dp(d+1,vector<ll>(d+1));
  dp[0][0]=1;
  for(int k=0;k<n;k++){
    int s=abs(p[k]-q[k]);
    vector<vector<ll>>dp2(d+1,vector<ll>(d+1)),dp3(d+1,vector<ll>(d+1)),nxt(d+1,vector<ll>(d+1));
    for(int i=0;i<=d;i++)
      for(int j=0;j<=d;j++){
        dp2[i][j]=dp[i][j];
        if(i!=0&&j!=d) dp2[i][j]=(dp2[i][j]+dp2[i-1][j+1])%mod;
      }
    for(int i=0;i<=d;i++)
      for(int j=0;j<=d;j++){
         int si=i,sj=j-s;
         if(sj<0) si+=sj,sj=0;
         if(si>=0&& si<=d && sj>=0){
            nxt[i][j]=(nxt[i][j]+dp2[si][sj])%mod;
         }
         int ti=i-(s+1),tj=j+1;
         if(ti>=0&&tj<=d){
           nxt[i][j]=(nxt[i][j]-dp2[ti][tj]+mod)%mod;
         }
      }
    for(int i=0;i<=d;i++)
      for(int j=0;j<=d;j++){
        dp3[i][j]=dp[i][j];
        if(i!=0&&j!=0){
          dp3[i][j]=(dp3[i][j]+dp3[i-1][j-1])%mod;
        }
        if(i+1<=d&&j+s+1<=d){
          nxt[i+1][j+s+1]=(nxt[i+1][j+s+1]+dp3[i][j])%mod;
        }
        if(i+s+1<=d&&j+1<=d){
          nxt[i+s+1][j+1]=(nxt[i+s+1][j+1]+dp3[i][j])%mod;
        }
      }
     
    dp=nxt;
  }
  ll ans=0;
  for(int i=0;i<=d;i++)
    for(int j=0;j<=d;j++)
      ans=(ans+dp[i][j])%mod;
  cout<<ans<<'\n';
}

G.012 Inversion(线段树)

Problem

给定一个长度为\(N\)并且只包含\(0、1、2\)的序列\(A\),进行\(Q\)次操作,每次操作有两种类型

  • 1 L R:输出区间\([L,R]\)之间的逆序对数量
  • 2 L R S T U:对每一个\(L\le i \le R\),如果\(A_i\)是\(0\),就把\(A_i\)变成\(S\),如果是\(1\),就变成\(T\),如果是\(2\),就变成\(U\)。

\(1\le N,Q\le 10^5\),\(0\le A_i,S,T,U\le 2\)

Solve

线段树维护一下量

  • 区间中\(0、1、2\)的数量
  • 区间中\(10、20、21\)的数量(其实是逆序对组合的数量,容易发现只有三种组合)

由于有区间修改,所以要下传标记,考虑如何下传。
我们下传标记,其实就是记录原来区间内\(0、1、2\)分别变成了什么。一开始我们使用一个\(\text{lazy}[3]\)来表示,初始肯定是\(\text{lazy}[0]=0,\text{lazy}[1]=1,\text{lazy}[2]=2\)。现在考虑对一个区间进行两次修改,比如先把\(2\)变成\(1\),再把\(1\)变成\(0\),在第一次修改中,\(\text{lazy}[2]=1\),第二次修改,由于修改了\(1\),但原来的\(2\)都变成了\(1\),就变成了一个链式传递关系,此时\(\text{lazy}[2]=\text{op}[\text{lazy}[2]]\),其中\(\text{op}[i]\)表示这次操作的时候把\(i\)变成了\(\text{op}[i]\)。下放标记的时候,对于两个儿子来说,父亲的\(\text{lazy}\)数组就变成了\(\text{op}\)数组

Code

#include <bits/stdc++.h>
#define LL long long
#define ls rt<<1
#define rs rt<<1|1
using namespace std;
const int N=1e5+10;
struct node{
  LL num1[3];
  LL num2[3][3];
  int tag,lazy[3];
}tr[N*4];
int a[N];
int c[3],tt[3];
LL tmp[3][3];
void pushup(node &rt,node ll ,node rr){
  for(int i=0;i<3;i++) rt.num1[i]=ll.num1[i]+rr.num1[i];
  for(int i=0;i<3;i++)
    for(int j=0;j<3;j++)
      if(i!=j)
       rt.num2[i][j]=ll.num2[i][j]+rr.num2[i][j]+ll.num1[i]*rr.num1[j];
}
void build(int rt,int l,int r){
     for(int i=0;i<3;i++) tr[rt].lazy[i]=i;
     if(l==r){
       tr[rt].num1[a[l]]++;
       return;
     }
     int mid=(l+r)>>1;
     build(ls,l,mid);
     build(rs,mid+1,r);
     pushup(tr[rt],tr[ls],tr[rs]);
}
void update(int rt,int q[]){
   for(int i=0;i<3;i++) tt[i]=0;
   for(int i=0;i<3;i++)
    for(int j=0;j<3;j++) tmp[i][j]=0;

   for(int i=0;i<3;i++) tr[rt].lazy[i]=q[tr[rt].lazy[i]];
   for(int i=0;i<3;i++) tt[q[i]]+=tr[rt].num1[i];
   for(int i=0;i<3;i++) tr[rt].num1[i]=tt[i];

   for(int i=0;i<3;i++)
    for(int j=0;j<3;j++)
      if(q[i]!=q[j]) tmp[q[i]][q[j]]+=tr[rt].num2[i][j];
   for(int i=0;i<3;i++)
    for(int j=0;j<3;j++) 
      tr[rt].num2[i][j]=tmp[i][j];
   tr[rt].tag=1;
}
void pushdown(int rt){
  if(tr[rt].tag){
      update(ls,tr[rt].lazy);
      update(rs,tr[rt].lazy);
      tr[rt].tag=0;
      for(int i=0;i<3;i++) tr[rt].lazy[i]=i;
  }
}
void modify(int rt,int L,int R,int l,int r){
    if(l<=L&&R<=r){
       update(rt,c);
       return;
    }
    pushdown(rt);
    int mid=(L+R)>>1;
    if(l<=mid) modify(ls,L,mid,l,r);
    if(r>mid) modify(rs,mid+1,R,l,r);
    pushup(tr[rt],tr[ls],tr[rs]);
}
node query(int rt,int L,int R,int l,int r){
   if(l<=L&&R<=r){
      return tr[rt];
   }
   pushdown(rt);
   int mid=(L+R)>>1;
   if(r<=mid) return query(ls,L,mid,l,r);
   else if(l>mid) return query(rs,mid+1,R,l,r);
   else{
     node res;
     node ll=query(ls,L,mid,l,mid),rr=query(rs,mid+1,R,mid+1,r);
     pushup(res,ll,rr);
     return res;
   }
}
int main(){
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  int n,q;
  cin>>n>>q;
  for(int i=1;i<=n;i++) cin>>a[i];
  build(1,1,n);
  for(int i=1;i<=q;i++){
      int op;
      cin>>op;
      if(op==1){
        int l,r;
        cin>>l>>r;
        node t=query(1,1,n,l,r);
        LL res=0;
        res=t.num2[1][0]+t.num2[2][0]+t.num2[2][1];
        cout<<res<<'\n';
      }else{
        int l,r;
        cin>>l>>r;
        for(int i=0;i<3;i++) cin>>c[i];
        modify(1,1,n,l,r);
      }
  }
}

标签:le,int,text,ll,cin,ABC,265,dp
来源: https://www.cnblogs.com/Arashimu0x7f/p/16612437.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有