2019牛客国庆集训派对day5E题

题目链接:https://ac.nowcoder.com/acm/contest/1110/E

题意理解:

给出一组数字序列,求每次删掉一个数字之后序列的所有 f[i]^2的异或和,f[i]表示以i为结尾的LIS长度。

题解:

第一眼是直接LIS暴力来做,不能用经典的LIS做法来求,O(n^3)肯定会炸,然后想了想,可以用O(nlogn)二分贪心优化的LIS来求 f 数组,本来以为就这么简单,但是过不去,叉姐专门把带log的给卡了,因此只能用O(n^2)的方法来做了。

这个题需要深刻理解LIS的含义,只有知道dp数组的意义才能想到O(n^2)的做法。

删除第i个数,那么对之前的数 f 值是没有影响的,而在其之后的值有两种情况:假设当前为f[j],那么之后的可能为f[j]-1或者f[j];

1.当为f[j]的时候,表示j之前有一个位置f[x]=f[j]-1而且a[x]<a[j] ( x!=i)

2.当为f[j]-1的时候,表示j之前没有这种位置。

然后我们可以维护一个数组dp[i],用来表示在f值为i时的最小a[](即当前长度为 i 时最后的元素最小可以是多少)

之后我们删除 i 的时候判断一下是否对 j 的上升子序列有影响。尽可能贪心地维护最小值,保证f[j]仍为f[j]的情况多。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=5e3+7;
const int INF=0x3f3f3f3f;
int a[maxn];
int f[maxn];
int dp[maxn];
int b[maxn];
int main(){
	int n;
	while(~scanf("%d",&n)){
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]);
		for(int i=1;i<=n;i++){
			f[i]=1;
			for(int j=1;j<i;j++)
				if(a[j]<a[i])
					f[i]=max(f[i],f[j]+1);
		}
		for(int i=1;i<=n;i++){
            memset(dp,INF,sizeof dp);
            dp[0]=0;
            int res=0;
            for(int j=1;j<=i-1;j++){
                dp[f[j]]=min(dp[f[j]],a[j]);
                res^=f[j]*f[j];
            }
            for(int j=i+1;j<=n;j++){
                int temp=f[j];
                if(dp[temp-1]<a[j]){
                    dp[temp]=a[j];
                    res^=temp*temp;
                }
                else{
                    dp[temp-1]=a[j];
                    res^=(temp-1)*(temp-1);
                }
            }
            printf("%d ",res);
        }
        printf("\n");
	}
	return 0;
}

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值