多校联考A层2 二分图排列 最短路问题 捡石子游戏 凹函数

T1【记录路径转移类型DP】给你一个排列A,定义如果ai<aj,i>j,那么在i和j之间连接一条边,把序列转化成一个图,如果图是二分图,则合法。让你把序列中一些数变成相反数使得成为二分图,要求修改方案字典序最小。(n<=1e6)

考场

题目没理解清楚。死在这了。

正解

如果不存在奇环,那么一定不存在长度>2的下降序列,根据dilworth定理,
如果最长下降子序列长度是0/1/2,等价于最少可以用0/1/2个最长上升子序列组成这个序列,所以考虑构造2个合法上升子序列,而且负数前缀最多。为了方便构造,倒着DP
\(dp[i][0/1]表示ai为负数/正数结尾,另一个链的末尾(最靠近ai的后面)最大值\),转移考虑把ai-1放到正连里,或者放到反连里。
正着构造,维护2个上升序列,第二个是为了留出最多的合法空间的殿后链,尽量小。(必须要满足-ai>dp[i][0],否则last-- -ai----dp[i][0]不合法)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=a;i<=b;++i)
#define f_(i,a,b) for(register int i=a;i>=b;--i)
#define chu printf
#define ll long long
#define rint register int
#define ull unsigned long long
inline ll re()
{
    ll x=0,h=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')h=-1;
        ch=getchar();
    }
    while(ch<='9'&&ch>='0')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*h;
}
const int N=510;
int dp[(int)1e6+100][2];
int n,T,a[(int)1e6+100];
inline void Max(int&u,int v)
{
    if(u<v)u=v;
}
int main()
{
 // freopen("1.in","r",stdin);
  // freopen("a.out","w",stdout);
    T=re();
    while(T--)
    {
        n=re();
        _f(i,1,n)a[i]=re();
        _f(i,0,n)
        _f(j,0,1)dp[i][j]=-1e9;
        dp[n][0]=dp[n][1]=1e9;
        bool ilegal=0;
        f_(i,n,2)
        {
            if(a[i-1]<a[i])Max(dp[i-1][1],dp[i][1]);
            if(-a[i-1]<a[i])Max(dp[i-1][0],dp[i][1]);
            if(-a[i-1]<-a[i])Max(dp[i-1][0],dp[i][0]);
            if(a[i-1]<dp[i][0])Max(dp[i-1][1],-a[i]);
            if(a[i-1]<dp[i][1])Max(dp[i-1][1],a[i]);
            if(-a[i-1]<dp[i][0])Max(dp[i-1][0],-a[i]);
            if(-a[i-1]<dp[i][1])Max(dp[i-1][0],a[i]);
            if(dp[i-1][0]==-1e9&&dp[i-1][1]==-1e9)
            {ilegal=1;break;}
        }
        if(ilegal){chu("NO\n");continue;}
        chu("YES\n");

        int l1=-1e9,l2=-1e9;
        _f(i,1,n)
        {
            if(-a[i]>l1)
            {
                l1=-a[i];
                chu("%d ",l1);
            }
            else if(-a[i]>l2&&-a[i]<dp[i][0])
            {
                l2=-a[i];
                chu("%d ",l2);
            }
            else if(a[i]>l1)
            {
                l1=a[i];
                chu("%d ",l1);
            }
            else 
            {
                l2=a[i];
                chu("%d ",l2);
            }
        }
        chu("\n");
    }
    return 0;
}
/*
4
3
1 2 3
6 
1 3 2 6 5 4
4 
4 1 3 2
8 
3 2 1 6 7 8 5 4
*/

T3【博弈论】作为专题整理,见数论

T4【平面几何转DP+可行性和最优性优化】要求你构造一个凹函数,使得定义域在[0,n]值域在[0,m]的整点尽量多。n<=3000

转化成从小到大选择若干个向量使得sigma_x=n,sigma_y=m。
首先暴力\(O(n^4)\)枚举最后的(x,y)的和,然后枚举(i,j)向量。
其次优化,就是神奇的证明了最终合法状态的向量个数<=\(n^{2/3}\),九十<=320,非常少,考虑对DP维护对象转化,
dp[i][j]表示x的和值是i,选择了j个向量,y的最小值。在转移的时候贪心地强制规定如果选择了(x,y)向量,那么(1x,1y)的合法的必须都选,假设有一个不选,那么显然置换更优,注意是强制,不一定真的选了,如果没选,说明(x,y)一定不更优,就跳过了,所以sum'累加必须加上。
在DP(x,y)的时候,n倒序保证了(x,y)只使用一次(为什么gcd(x,y)=1?如果不是,那么(x,y)变成(x/2,y/2)*2更优,但是不合法),size记录(i)状态的j边界,每次加入一个数j最多变多1,所以udpate边界。为甚么边界一次一次加?否则后边都是0,size一直加就会边界无线拓展。但是实际上一次性初始化也可以

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=a;i<=b;++i)
#define f_(i,a,b) for(register int i=a;i>=b;--i)
#define chu printf
#define ll long long
#define rint register int
#define ull unsigned long long
inline ll re()
{
    ll x=0,h=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')h=-1;
        ch=getchar();
    }
    while(ch<='9'&&ch>='0')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*h;
}
const int N=510;
int dp[3100][N],size[3100],sum[3100][2],mx;
struct Npde
{
    int n,m;
}e[100000+10];
int q;
inline void ins(int x,int y)
{
    f_(i,mx,x)
    {
        _f(j,0,size[i-x])
        {
            int ret=dp[i-x][j]+y;
            if(ret>mx)break;
            dp[i][j+1]=min(dp[i][j+1],ret);
        }
        if(dp[i][size[i]+1]!=0x3f3f3f3f)
        {
            dp[i][++size[i]+1]=0x3f3f3f3f;
        }
    }
}
inline int gcd(int x,int y)
{
    return (y==0)?x:(gcd(y,x%y));
}
int main()
{
 //  freopen("1.in","r",stdin);
 //  freopen("a.out","w",stdout);
  q=re();
  _f(i,1,q)
  {
      e[i].n=re(),e[i].m=re();
      mx=max({e[i].n,e[i].m,mx});
  }
  _f(i,0,mx)dp[i][1]=0x3f3f3f3f;
  _f(i,1,mx)
  {
      int sum1=0,sum2=0;
      for(rint j=1;j<=mx&&max(sum[j][0],sum[j][1])<=mx;++j)
      {
          if(gcd(i,j)==1)
          {
              sum1+=i,sum2+=j;
              if(max(sum[j][0]+sum1,sum[j][1]+sum2)<=mx)ins(i,j);
          }
          sum[j][0]+=sum1;
          sum[j][1]+=sum2;
      }
  }
  _f(i,1,q)
  {
      chu("%ld\n",upper_bound(dp[e[i].n],dp[e[i].n]+size[e[i].n]+1,e[i].m)-dp[e[i].n]);
  }
    return 0;
}
/*
2
3 2
1 1
*/
posted on 2022-10-03 20:22  HZOI-曹蓉  阅读(23)  评论(0编辑  收藏  举报