A.Recycling Center
思路:n给的很小,所以考虑贪心.如果还剩重量小于等于c的垃圾袋,直接选他们重量最大的,否则剩下的未选垃圾袋都要一个硬币
#include<iostream>
#include<queue>
#include<map>
#include<iomanip>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#include<functional>
#define ll long long
#define ull unsigned long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans * x%mod;
}
x = x * x%mod;
y >>= 1;
}
return ans%mod;
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
struct s{
// ll p,w,c;
ll c,d;
friend bool operator<(const s&a,const s&b){
return a.c>b.c;
}
};
// ll zs[2500000];
// ll cnt=0;
// void ola(ll x)
// {
// vector<bool>vis(x+5);
// for(ll i=2;i<=x;i++)
// {
// if(!vis[i])zs[++cnt]=i;
// for(ll j=1;i*zs[j]<=x;j++)
// {
// vis[i*zs[j]]=1;
// if(i%zs[j]==0)break;
// }
// }
// }
int main(){
fio();
ll t=1;
cin>>t;
while(t--){
ll n,c;
cin>>n>>c;
vector<pair<ll,ll>>a(n+2);
vector<bool>vis(33,0);
for(ll i=1,x;i<=n;i++){
cin>>x;
a[i]={x,i};
}
// ll cc=n;
ll ans=0;
ll cc=n;
while(1){
sort(a.begin()+1,a.begin()+1+n);
bool flag=0;
ll id=0;
for(ll i=1;i<=n;i++){
if(vis[a[i].second])continue;
else if(a[i].first<=c){
id=i;
}
}
if(id){
vis[a[id].second]=1;
for(ll i=1;i<=n;i++)a[i].first*=2;
}
else {
ans+=cc;
break;
}
cc--;
}
cout<<ans<<endl;
}
return 0;
}
B.Deque Process
思路:可以选择类似震荡的构造,就是忽然大忽然小,这个是一定对的,因为所选的最左的数和最右的数大小不一样,那么这次选了,那么下次更大或者更小的数将改变震荡方向。所以逢奇选最大的数,逢偶选最小的数即可
#include<iostream>
#include<queue>
#include<map>
#include<iomanip>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#include<functional>
#define ll long long
#define ull unsigned long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans * x%mod;
}
x = x * x%mod;
y >>= 1;
}
return ans%mod;
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
struct s{
// ll p,w,c;
ll c,d;
friend bool operator<(const s&a,const s&b){
return a.c>b.c;
}
};
// ll zs[2500000];
// ll cnt=0;
// void ola(ll x)
// {
// vector<bool>vis(x+5);
// for(ll i=2;i<=x;i++)
// {
// if(!vis[i])zs[++cnt]=i;
// for(ll j=1;i*zs[j]<=x;j++)
// {
// vis[i*zs[j]]=1;
// if(i%zs[j]==0)break;
// }
// }
// }
int main(){
fio();
ll t=1;
cin>>t;
while(t--){
ll n;
cin>>n;
vector<ll>a(n+2);
for(ll i=1;i<=n;i++){
cin>>a[i];
}
ll l=1,r=n;
for(ll i=1;i<=n;i++){
if(i&1){
if(a[l]>a[r])cout<<"L",l++;
else cout<<"R",r--;
}
else {
if(a[l]>a[r])cout<<"R",r--;
else cout<<"L",l++;
}
}
cout<<endl;
}
return 0;
}
C. Leftmost Below
思路:无论如何,当我要构成第i位的正确答案时,前i-1位必须已经正确。随后我们可以发现,其增加规律是x,x+1,x+x+1+1,x+x+1+1+1...最小递增规律。显然如果我选则x,x+1且x*2+1等于a[i]将是最优秀的叠加方法(其他的要么构造不出来,要么会出现一个数大于此时选的两个数,那还不如这样更优),直接分奇偶模拟即可(注意结合前面的最小值约束)
证明:假设limit很大。1.如果第a[i-1]大于等于a[i],我们先构造好a[i],那么a[i-1]一定已经加了某些值(不妨设为x),下一次加的值最小是x+1,那么这仍然符合上述思路给出的递增规律,如果有个值太大将会使a[i]变大,那最优秀还是用类似一半一半的加法是最优的。如果此时x+x+1会等于a[i],x+1<=a[i],那么确实可以先构造好a[i]然后构造ai-1,但是这与先构造a[i-1],然后构造a[i]没有区别。如果x+1>a[i]显然GG,但是原本我是可以先构造a[i-1],再构造a[i]的,所以此时必须先构造好a[i-1],然后构造a[i]才行;2.如果a[i-1]小于a[i],能先构造出a[i],然后构造出a[i-1]的情况不可能出现(请自己枚举下如6,7。7,8.细节其实还是和递增规律有关)。所以直接构造a[i-1],再构造a[i]是必然的。综上如果答案正确(可以推广到前面去),先构造第i-1位再构造第i位是一定对的,那么推广下,思路中的命题成立。limit只会限制你加多少(根据递增规律来看,最好构造且符合答案的递加方法是类似于一半一半的加)。
吐槽:把等于的情况判断错了,wa了一发,还想了半天为什么不对QwQ
#include<iostream>
#include<queue>
#include<map>
#include<iomanip>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#include<functional>
#define ll long long
#define ull unsigned long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans * x%mod;
}
x = x * x%mod;
y >>= 1;
}
return ans%mod;
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
struct s{
// ll p,w,c;
ll c,d;
friend bool operator<(const s&a,const s&b){
return a.c>b.c;
}
};
// ll zs[2500000];
// ll cnt=0;
// void ola(ll x)
// {
// vector<bool>vis(x+5);
// for(ll i=2;i<=x;i++)
// {
// if(!vis[i])zs[++cnt]=i;
// for(ll j=1;i*zs[j]<=x;j++)
// {
// vis[i*zs[j]]=1;
// if(i%zs[j]==0)break;
// }
// }
// }
int main(){
fio();
ll t=1;
cin>>t;
while(t--){
ll n;
cin>>n;
vector<ll>a(n+2);
for(ll i=1;i<=n;i++)cin>>a[i];
ll limit=1e18;
bool flag=0;
for(ll i=1;i<=n;i++){
if(a[i]>limit){
if(a[i]&1){
if(a[i]/2>limit||(a[i]/2+1)>limit){
flag=1;
break;
}
}
else {
if(a[i]/2-1>limit||a[i]/2+1>limit){
flag=1;
break;
}
}
}
limit=min(limit,a[i]);
}
if(flag)cout<<"NO"<<endl;
else cout<<"YES"<<endl;
}
return 0;
}
D.Sum of LDS
思路:有点小诈骗?,考虑题目给的条件max(\(p_{i-2}\),\(p_{i-1}\))>\(p_i\)。通过简单枚举下我们可以发现,可以转为dp问题(第i位只需从i-1转或者i-2转)。设dp[i]为所选右边界为i,左边界任选的所有答案数。如果第i位前面两个数无比它大的数,此时i=1或者i=2,此时dp[i]直接算即可(不用我说吧)。否则接看位置:如果是\(p_{i-1}>p_i\),那么dp[i]=max(dp[i],dp[i-1]+i-1+1)(直接延长上一位的边界+单独的[i,i]).如果是\(p_{i-2}>p_i\),那么dp[i]=max(dp[i],dp[i-2]+i-2+2)(延长位置为i-2的答案,然后考虑[i-1,i],[i,i],这两个答案为1).然后边走边加上答案即可。
#include<iostream>
#include<queue>
#include<map>
#include<iomanip>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#include<functional>
#define ll long long
#define ull unsigned long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans * x%mod;
}
x = x * x%mod;
y >>= 1;
}
return ans%mod;
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
struct s{
// ll p,w,c;
ll c,d;
friend bool operator<(const s&a,const s&b){
return a.c>b.c;
}
};
// ll zs[2500000];
// ll cnt=0;
// void ola(ll x)
// {
// vector<bool>vis(x+5);
// for(ll i=2;i<=x;i++)
// {
// if(!vis[i])zs[++cnt]=i;
// for(ll j=1;i*zs[j]<=x;j++)
// {
// vis[i*zs[j]]=1;
// if(i%zs[j]==0)break;
// }
// }
// }
int main(){
fio();
ll t=1;
cin>>t;
while(t--){
ll n;
cin>>n;
vector<ll>a(n+3);
for(ll i=1;i<=n;i++)cin>>a[i];
vector<ll>dp(n+3,0);
// dp[0]=1;
ll ans=0;
for(ll i=1;i<=n;i++){
if(i==1){
dp[i]=1;
}
else if(i==2){
if(a[i-1]>a[i])dp[i]=dp[i-1]+2;
else dp[i]=2;
}
else {
if(a[i-1]>a[i])dp[i]=dp[i-1]+i-1+1;
if(a[i-2]>a[i])dp[i]=max(dp[i],dp[i-2]+i-2+1+1);
}
// cout<<dp[i]<<endl;
ans+=dp[i];
}
cout<<ans<<endl;
}
return 0;
}
思路:看了会,发现还是二分。我们可以这样,假定一个数mid,然后a数组中的数大于等于它即为1,否则为-1,那么根据题目给出的中位数要求,我们只需要看是否存在长度大于等于k的子数组,且子数组之和的大于等于0.只要存在l就变大,否则r变小。怎么去解决check呢?类似dp的做法,从左往右做个最大连续值dp(设为pre),从右往左做个最大连续dp(设为sub),然后再做个前缀和(设为cc),然后枚举i,取max(cc[i+k-1]-cc[i-1]+pre[i-1]+sub[i+k]),如果最大值大于等于0即可根据刚刚说法得出二分的趋势。如何判定区间,确定好最后答案后,从左往右扫一遍,第一次出现cc[i+k-1]-cc[i-1]+pre[i-1]+sub[i+k]>=0的i即为左边界,然后根据左边界和k的大小遍历找右边界即可。
#include<iostream>
#include<queue>
#include<map>
#include<iomanip>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#include<functional>
#define ll long long
#define ull unsigned long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans * x % mod;
}
x = x * x % mod;
y >>= 1;
}
return ans % mod;
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
struct s {
// ll p,w,c;
ll c, d;
friend bool operator<(const s& a, const s& b) {
return a.c > b.c;
}
};
// ll zs[2500000];
// ll cnt=0;
// void ola(ll x)
// {
// vector<bool>vis(x+5);
// for(ll i=2;i<=x;i++)
// {
// if(!vis[i])zs[++cnt]=i;
// for(ll j=1;i*zs[j]<=x;j++)
// {
// vis[i*zs[j]]=1;
// if(i%zs[j]==0)break;
// }
// }
// }
int main() {
fio();
ll t = 1;
cin >> t;
while (t--) {
ll n, k;
cin >> n >> k;
vector<ll>a(n + 2);
for (ll i = 1; i <= n; i++) {
cin >> a[i];
}
ll l = 1, r = n;
ll al=-1;
function<ll(ll)>ck = [&](ll x) {
vector<ll>pre(n + 2, 0), sub(n + 2, 0), cc(n + 2, 0);
for (ll i = 1; i <= n; i++) {
if (a[i] >= x) {
pre[i] = max(pre[i - 1] + 1, 1ll);
}
else {
pre[i] = max(pre[i - 1] - 1, 0ll);
}
cc[i] = cc[i - 1] + (a[i] >= x ? 1 : -1);
}
for (ll i = n; i >= 1; i--) {
if (a[i] >= x) {
sub[i] = max(sub[i + 1] + 1, 1ll);
}
else {
sub[i] = max(sub[i + 1] - 1, 0ll);
}
}
ll d = -1e18;
for (ll i = 1; i <= n - k + 1; i++) {
ll r = i + k - 1;
d = max(pre[i - 1] + sub[r + 1] + cc[r] - cc[i-1], d);
if(d>=0&&al==-1)al=i;
}
return d >= 0;
};
while (l <= r) {
ll mid = l + r >> 1;
if (ck(mid)) {
l = mid + 1;
}
else r = mid - 1;
}
al=-1;
ll ar;
ll u=ck(r);
ll cc=0;
for(ll i=al;i<=n;i++){
cc+=(a[i]>=r?1:-1);
if(i>=al+k-1&&cc>=0){
ar=i;
break;
}
}
cout<<r<<" "<<al<<" "<<ar<<endl;
}
return 0;
}