CSP-S模拟9 最长上升子序列 独特序列 最大GCD 连续子段

DP+思维

T1:给出n,K,和递增序列a1--k,构造1~n的排列,使得a是它的最长上升子序列。(n<=2e5)

容易想到在ai~a(i+1)之间插入<ai的数,在a(k-1) ~ a(k)之间倒序插入>ak的数,。-->10tps
正解:考虑
7 2
3 7
you will make:3 2 1 7 5 4
but is better: 3 1 7 5 4 2
也就是说每ai~ai+1之间只要放1个<ai的数,最后所有剩下的<ak的数只要倒序放进原序列里就行。
为什么倒序?
考虑当a的项都很小,最后剩下的数ak>Min>a1k-1,那么对于任意倒序正过来,都可以和a1k-1构成长度k+1的序列,不合法。
考虑当a的项都很大剩下的数Min<a(x)(x<<k),那么一定保证插在空位里的都有数,同样空位的数+剩下的数任意正序长度>=k+1,不合法,所以必须倒序。

点击查看代码


#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=2e5+100;
int n,K,a[N],num[N],cnt,no[N];
int main()
{
  // freopen("1.in","r",stdin);
   //freopen("a.out","w",stdout);
   n=re(),K=re();
   _f(i,1,K)a[i]=re(),num[a[i]]=1;
   _f(i,1,n)
   if(!num[i])no[++cnt]=i;
   int now=1;
   _f(i,1,K-1)
   {
       chu("%d ",a[i]);
       if(now<=cnt&&no[now]<a[i])
       {
           chu("%d ",no[now]);++now;
       }
   }
   while(cnt>=now&&no[cnt]>a[K])
   {
       chu("%d ",no[cnt]);--cnt;
   }
    chu("%d ",a[K]);
    while(cnt>=now)chu("%d ",no[cnt]),--cnt;
    return 0;
}
/*
*/

T2:给出长度n的序列,求n的子序列使得取出数的方法唯一。(n<=2e5)

最优子结构:子序列S唯一,当且仅当在前i项出现的S_son唯一。
\(dp[i]:表示前i项,选i的唯一序列个数\)
dp[i]=sigma(dp[j])+(pre[j]==0) (pre[i]<=j<i &&sub[j]>i)
\(O(n^2)\)
树状数组优化,每到一个位置,前缀和查询query(i)-query(pre[i]-1)
,删除这个数上一次出现,为了保证sub[j]>i,其实就是对于所有sub出现的都删掉了。

点击查看代码


#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;
}
#define int ll
const int N=2e5+100;
const ll mod=998244353;
int n,a[N],dp[N],pre[N],lst[N],sub[N],low[N];
inline void upd(int&x,int y)
{
    x=(ll)((ll)x+(ll)y+mod)%mod;
}
inline int query(int x)
{
    int ans=0;
    while(x)
    {
        ans+=low[x];
        x-=((x)&(-x));
    }
    return ans;
}
inline void update(int x,int val)
{
    while(x<=n)
    {
        low[x]+=val;
        x+=(x&(-x));
    }
}
signed main()
{
  // freopen("1.in","r",stdin);
   //freopen("a.out","w",stdout);
    n=re();
    _f(i,1,n)
    {
        a[i]=re();
        pre[i]=lst[a[i]];
        lst[a[i]]=i;
    }
    _f(i,1,n)lst[i]=n+1;
    f_(i,n,1)
    {
        sub[i]=lst[a[i]];
        lst[a[i]]=i;
    }
  //  _f(i,1,n)chu("dp[%d]:%d\n",i,dp[i]);
    int ans=0;
    _f(i,1,n)
    {
        upd(dp[i],query(i-1));
        if(!pre[i])upd(dp[i],1);
        if(pre[i])//如果前面有重复出现的
        {
            upd(dp[i],-query(pre[i]-1));
            update(pre[i],-dp[pre[i]]);//上个位置就没有贡献了
        }
        update(i,dp[i]);
     //   chu("update[%lld]:%lld\n",i,dp[i]);
    }
    f_(i,n,1)
    {
        if(sub[i]>n)upd(ans,dp[i]);
    }
    chu("%lld",ans);
    return 0;
}
/*
*/

T3:给出n个数,K,可以进行K次给某个序列里的数+1的操作,求gcd最大值。(n<=2e5)

容易发现K>sum,函数关于K递增。
当K<sum,考虑O(n^logn)求出确定gcd(gcd_now<=gcd<Max)后Calc值。
预处理整除和取模后的值和,然后查询时加减即可。

点击查看代码




#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=3e5+100;
#define int ll
ll n,K,a[N],Max,Min,should,ans,sum[N],Sum;
inline ll gcd(ll x ,ll y)
{
	if(!y)return x;
	return gcd(y,x%y);
}
int buc[N],sbuc[N],f[N],g[N];
//f[x]:刚好整除x的数个数
//g[x]:/x剩下的数
inline void calc(int mod)
{
	for(int i=mod;i<=Max;i+=mod)
	{
		int l=i,r=min(Max,l+mod-1);
		g[mod]+=(sbuc[r]-sbuc[l-1])*i/mod;
	}
}
inline ll Ok(int mod)
{
	ll Cnt=0;
	Cnt=n*mod-(Sum-g[mod]*mod);
	Cnt-=f[mod]*mod;
//	chu("%lld cost:%lld\n",mod,Cnt);
	return Cnt<=K;
}
signed main()
{
   //freopen("1.in","r",stdin);
   //freopen("a.out","w",stdout);
   n=re(),K=re();
   Min=1e10;
   _f(i,1,n) 
   {
	   a[i]=re();buc[a[i]]++;
	   Sum+=a[i];
	   Max=max(a[i],Max);
	  // Min=min(a[i],Min);
   }
   //尝试都补成Max
   should=0;int yes=1;
   _f(i,1,n)
   {
	   should+=Max-a[i];
	   if(should>K)
	   {
		   yes=0;break;
	   }
   }
   if(yes)
   {
	   ans=(K-should)/n+Max;
	   chu("%lld",ans);
	   return 0;
   }
    Min=a[1];
    _f(i,2,n)
	{
		Min=gcd(Min,a[i]);
	}
	ans=Min;
   _f(i,1,Max)sbuc[i]=sbuc[i-1]+buc[i];
   _f(i,1,Max)
   {
	   for(ll j=1;j*i<=Max;++j)
	   f[i]+=buc[j*i];
   }
   _f(i,Min+1,Max-1)
   calc(i);
   //_f(i,Min+1,Max-1)
 //  chu("g[%d]:%lld\n",i,g[i]);
	//chu("%lld",ans);
	_f(i,Min+1,Max-1)
	{
		if(Ok(i))ans=i;
	}
	chu("%lld",ans);
    return 0;
}
/*
5 12345
1 2 3 4 5

3 6
3 4 9

3 4
30 10 20
*/

T4:n序列,K,求最少交换次数,使得n中出现连续1--K。(n<=2e5)

\(dp[i][j]:表示考虑前i个数,已经交换出来的数的状态集合是j的最小交换步数\)
考虑J是已经达到的K集合\(1__5__2__4_3_\)可能是这样,那么代价来自2部分
(1)内部逆序对,在每次加数的时候算出对于x>x的数个数累加(2)交换成连续,把空位补齐,要么已经选择的右移,要么没选择的左移,取一个最小就行。
\(dp[i][j|Si]=min(dp[i-1][j|Si]+move[j|Si],dp[i-1][j]+bit[j>>Si])\)
压缩成一维,需要倒序,因为对于大的状态,我先强制空位,然后考虑加数。

点击查看代码


#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=1<<16;
#define INF 0x3f3f3f3f
int dp[N+100],bit[N+100],you[N+100],n;
int main()
{
  // freopen("1.in","r",stdin);
   //freopen("a.out","w",stdout);
   n=re();int K=re();
   _f(i,1,(1<<K)-1)
   {
       bit[i]=bit[i>>1]+(i&1);
       you[i]=min(you[i],K-you[i]);
       you[i]=min(bit[i],K-bit[i]);
       //chu("t[%d]:%d\n",i,you[i]);
   }
   _f(i,1,1<<K)dp[i]=INF;
   dp[0]=0;
   _f(i,1,n)
   {
       int a=re()-1;
       f_(j,(1<<K)-1,0)
       {
          if(dp[j]!=INF)
          {
              if(((1<<a)&j)==0)dp[j|(1<<a)]=min(dp[j|(1<<a)],dp[j]+bit[j>>a]);
              dp[j]+=you[j];
          }
       }
   }
   chu("%d",dp[(1<<K)-1]);
    return 0;
}
/*
*/
posted on 2022-09-22 19:44  HZOI-曹蓉  阅读(68)  评论(0编辑  收藏  举报