Codeforces 815 (div2)D1 && 820 (div3)D 题解
题目大意是让求一个数组b的最大长度,数组b为数组a的下标(从0开始),其中对任意的
b
i
b_i
bi要满足:
a
b
i
a_{b_i}
abi^
b
i
+
1
b_{i+1}
bi+1 <
a
b
i
+
1
a_{b_{i+1}}
abi+1^
b
i
b_i
bi
很明显的dp问题,但是数据范围较大,直接暴力时间复杂度为
n
2
n^2
n2,稳稳超时。那考虑下b序列有什么性质。我们可以发现
a
i
a_i
ai的范围很小,最大为200,因此其异或上一个值后最大值不会超过256,所以可以得出状态转移的范围不会超过256,因此我们可以不用循环很多就能得出最终解。
**定义
f
i
f_i
fi为选
a
i
a_i
ai时,
b
i
b_i
bi的最长长度,那么转移方程就很明显了:
f
i
f_i
fi=max(
f
i
f_i
fi ,
f
j
f_j
fj) (
a
j
a_j
aj^
i
i
i <
a
i
a_i
ai^
j
j
j)
时间复杂度为O(n*400), 可以通过此题。
code:
#include<bits/stdc++.h>
using namespace std;
//#define int long long
const int N=4e5+10;
int a[N],f[N];
void work()
{
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i]; f[i]=1;
}
for(int i=0;i<n;i++){
for(int j=max(0,i-400);j<i;j++){
if((a[j]^i)<(a[i]^j)) f[i]=max(f[i],f[j]+1);
}
}
cout<<*max_element(f,f+n)<<endl;
}
signed main()
{
int t;
cin>>t;
while(t--)
{
work();
}
return 0;
}
题目大意:有n个人出去吃饭,第
i
i
i个人想花
a
i
a_i
ai的钱,但他实际有
b
i
b_i
bi的钱。
求最多能把这些人分成几组,每组最少两人,使得组内的
a
i
a_i
ai总和<=
b
i
b_i
bi总和。
思路:很明显的贪心问题,我们另
c
i
c_i
ci为
a
i
a_i
ai-
b
i
b_i
bi, 则可以转变为从
c
i
c_i
ci分组,使得组内和>=0 . **可以知道,任意两个>=0的
c
i
c_i
ci, 我们都可以分成一组,若对于所有
c
i
c_i
ci都大于0,则答案为n/2, 显然这样是最优的答案。关键是对负数怎么操作,那我们贪心的让组内最大值与最小值结合,若其相加>=0,则贪心的取这一对,若<0,则直接舍弃最小值,换为次小值继续结合。为什么是最优的呢? 因为一正一负结合,相当于看作省去了一个正数,而两正一负结合,不如直接两个正数结合, 因此我们排好序后,双指针模拟结合过程即可。
时间复杂度为O(n*logn)。
code:
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
#define x first
#define y second
const int N=2e5+10;
int a[N];
void work()
{
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
int x;
cin>>x;
a[i]=x-a[i];
}
sort(a+1,a+1+n);
int ans=0;
for(int i=1,j=n;i<j;){
if(a[i]+a[j]>=0) {
ans++; i++,j--;
}
else i++;
}
cout<<ans<<endl;
}
signed main()
{
int t;
cin>>t;
while(t--)
{
work();
}
return 0;
}