C. Chef Monocarp

这道题目给的tag实在是多,dp,二分图匹配,最小费用流都能写,反正由于数据量比较小,所以被各路神仙用各种方法过穿了,这里只写一下我看到的dp写法

dp[i][j]表示在排好序的a[i]数组中拿去i个元素且遍历到第j位时的状态,应该有如下转移方程

 $$dp[i][j]=min(dp[i-1][j-1]+ \left\vert a[i]-j \right\vert,dp[i][k](k\in[1,j-1]))$$

由于两个子元素不在一个阶层,所以dp[i][j]应该在自己的上一层被更新一次,之后再和自己同一阶层的靠前元素进行比较

#include<cstdio>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)kjh
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
typedef double db;
const ll mod=1000000007;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int inf=0x3f3f3f3f;
/* mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}*/
ll powmod(ll a,ll b) {ll res=1;a%=mod;  for(;b>0;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}

ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}

ll inv(ll a){return (powmod(a,mod-2)+mod)%mod;}

template<typename T>inline void read(T &x)
{
    T f=1;x=0;char c;
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())x=x*10+(c^48);
    x*=f;
}

const int N=4e2+50;

//set<pair<string,int> > st;
//状态转移方程 dp[i][j]=min(dp[i-1][j-1],dp[i][k]);
int _;
int dp[N][N],a[N];
int main() {
      for(scanf("%d",&_);_;_--){
          int m;
          cin>>m;
          memset(dp,0x3f,sizeof dp);
          rep(i,0,m)    scanf("%d",a+i);
          sort(a,a+m);
          dp[0][0]=0;
          rep(i,0,m+1)    {
                  rep(j,0,2*m){
                      dp[i][j+1]=min(dp[i][j],dp[i][j+1]);
                      if(i<m)
                      dp[i+1][j+1]=dp[i][j]+abs(a[i]-j-1);
                  }
          } 
          cout<<dp[m][2*m]<<endl;
    }
}

D. Minimal Height Tree

模拟,计算每一层能够存下的最多节点,存不下就depth++,进入下一层

#include<cstdio>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)kjh
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
typedef double db;
const ll mod=1000000007;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int inf=0x3f3f3f3f;
/* mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}*/
ll powmod(ll a,ll b) {ll res=1;a%=mod;  for(;b>0;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}

ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}

ll inv(ll a){return (powmod(a,mod-2)+mod)%mod;}

template<typename T>inline void read(T &x)
{
    T f=1;x=0;char c;
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())x=x*10+(c^48);
    x*=f;
}

const int N=2e5+50;

//set<pair<string,int> > st;
int a[N];
int _;
int main() {
      for(scanf("%d",&_);_;_--){
          int n;
          cin>>n;
          rep(i,0,n)    scanf("%d",a+i);
          int root=1;
          int cnt=1,depth=1,nxt=0,pre=0;
          rep(i,1,n){
              if(a[i]>pre||--root){
                  nxt++;
                 pre=a[i];    
//                 cout<<1<<endl;
            }
            else{
                depth++;
                root=nxt;
                nxt=1;
                pre=a[i];
//                cout<<2<<endl;
            }
        }
        cout<<depth<<endl;
    } 
}

E. Make It Increasing

从这一题又学到了一个新的模型,如何将一个序列快速变成升序列,构造+二分

 如果是处理非完全上升序列,就不用对a的元素进行预处理

#include<cstdio>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)kjh
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
typedef double db;
const ll mod=1000000007;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int inf=0x3f3f3f3f;
/* mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}*/
ll powmod(ll a,ll b) {ll res=1;a%=mod;  for(;b>0;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}

ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}

ll inv(ll a){return (powmod(a,mod-2)+mod)%mod;}

template<typename T>inline void read(T &x)
{
    T f=1;x=0;char c;
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())x=x*10+(c^48);
    x*=f;
}

const int N=5e5+50;

//set<pair<string,int> > st;
int a[N],b[N];
int m,n;
int cal(int l,int r,int mi,int mx){
    vector<int> ve;
    rep(i,l,r)
        if(a[i]>=mi&&a[i]<=mx){
            auto it=upper_bound(ve.begin(),ve.end(),a[i]);
            if(it==ve.end())    ve.push_back(a[i]);
            else *it=a[i];
        }
    return ve.size();
}
int main() {
      cin>>m>>n;
      rep(i,0,m){
          scanf("%d",a+i);
          a[i]-=i;
      }
    rep(i,0,n){
        scanf("%d",b+i);
        --b[i];
    }
    int ans=m-n;
    if(!n)    ans-=cal(0,m,-1e9,1e9);
    else {
        ans-=cal(0,b[0],-1e9,a[b[0]]);
        ans-=cal(b[n-1]+1,m,a[b[n-1]],1e9);
        rep(i,0,n-1){
            if(a[b[i]]>a[b[i+1]]){
                cout<<-1<<endl;
                return 0;
            }
            ans-=cal(b[i]+1,b[i+1],a[b[i]],a[b[i+1]]);
        }
    }
            cout<<ans<<endl;
}