好久没更新了,最近天天打杭电和牛客,题都没时间补了写题解就更没时间了
题意:
做法:每个选项最多答对 n n n道,如果出现的某个选项小于 n n n项,那么该选项最多答对数量应该是这些全对了,如果某个选项大于等于 n n n项,那么该选项最多答对数量应该是 n n n。把 A B C D ABCD ABCD四个选项的答案加起来就行。
c o d e : code: code:
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
const int N = 1;
int t;
int a[N];
string s;
int n,m,h,w;
int main()
{
cin>>t;
while(t--)
{
cin>>n;
cin>>s;
int numa=0;int numb=0;int numc=0;int numd=0;
int num1=0;int num2=0;int ans=0;
for(int i=0;i<s.size();i++)
{
if(s[i]=='A') numa++;
if(s[i]=='B') numb++;
if(s[i]=='C') numc++;
if(s[i]=='D') numd++;
}
if(numa<=n)
{
ans+=numa;
}
else ans+=n;
if(numb<=n)
{
ans+=numb;
}
else ans+=n;
if(numc<=n)
{
ans+=numc;
}
else ans+=n;
if(numd<=n)
{
ans+=numd;
}
else ans+=n;
printf("%d\n",ans);
}
return 0;
}
题意:
数据范围: N < = 2 ∗ 1 0 5 N<=2*10^5 N<=2∗105
做法:
先特判序列是否已经全为奇数或全为偶数。然后考虑有奇数也有偶数怎么做。
首先,两个奇数相加或者两个偶数相加奇偶性不变,只有奇数+偶数=奇数。
所以最后一定是把所有偶数变成了奇数,发现操作次数的下限就是偶数的个数。那么,如果序列种最大的数是奇数,则只需要让每个偶数跟这个最大的数操作一次,所有的偶数就可以变成奇数,答案为偶数的个数。
如果最大的数是偶数
A
i
A_i
Ai,不妨把数列先排序,我们先尽可能多地把偶数一次变成奇数。假设当前最大奇数是
c
n
t
cnt
cnt,目前序列仍然存在的最小偶数为
A
i
A_i
Ai,那么如果
c
n
t
>
A
i
cnt > A_i
cnt>Ai,就把
A
i
A_i
Ai变成
c
n
t
+
A
i
cnt+A_i
cnt+Ai,此时
c
n
t
cnt
cnt变成
c
n
t
+
A
i
cnt+A_i
cnt+Ai。如果
c
n
t
<
A
i
cnt<A_i
cnt<Ai,我们可以拿最大的偶数
m
a
x
o
d
d
maxodd
maxodd跟
c
n
t
cnt
cnt操作一次,把
c
n
t
cnt
cnt变成
c
n
t
+
m
a
x
o
d
d
cnt+maxodd
cnt+maxodd,再拿更新后的
c
n
t
cnt
cnt去跟剩余所有偶数去操作,此时
c
n
t
>
m
a
x
o
d
d
>
=
任意偶数
cnt>maxodd>=任意偶数
cnt>maxodd>=任意偶数,此情况即为最优解。我们在下限的基础上只多用了一次操作让最大奇数变成最大数。
c
o
d
e
:
code:
code:赛时写的有点丑陋
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#define int long long
using namespace std;
const int N = 2e5+5;
int t;
int a[N];
int vis[N];
int n,m,h,w;
signed main()
{
cin>>t;
while(t--)
{
scanf("%lld",&n);
int flag1=0;int flag2=0;
int maxn=0;
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)
{
if(a[i]%2) flag1++;
if(a[i]%2==0) flag2++;
maxn=max(maxn,a[i]);
vis[i]=0;
}//1 ji
if(flag1==0||flag2==0)
{
printf("0\n");
continue ;
}//特判全奇数/全偶数
sort(a+1,a+1+n);
int maxji=0;
for(int i=n;i>=1;i--)
{
if(a[i]%2)
{
maxji=a[i];//最大奇数
break;
}
}
int ans=0;
int fflag=0;
for(int i=1;i<=n;i++)
{
if(a[i]%2==0&&a[i]<maxji)
{
ans++;
maxji+=a[i];//注意最大奇数也要更新
vis[i]=1;
continue ;
}
if(a[i]%2==0&&a[i]>maxji)
{
fflag=1;
break;
}
}
if(!fflag)//所有偶数都更新过了
{
printf("%lld\n",ans);
continue ;
}
int maxou=0;
for(int i=n;i>=1;i--)
if(a[i]%2==0)
{
maxou=a[i];
vis[i]=1;
break;
}
maxji+=2*maxou;
ans+=2;
for(int i=1;i<=n;i++)
{
if(a[i]%2==0&&vis[i]==0)
{
ans++;
maxji+=a[i];
}
}
printf("%lld\n",ans);
}
return 0;
}
题意:
数据范围: N , K < = 2 ∗ 1 0 5 N,K<=2*10^5 N,K<=2∗105
做法:
发现如果存在合法情况,那么答案一定出现在
[
M
A
X
(
A
i
)
,
M
A
X
(
A
i
)
+
K
]
[MAX(A_i),MAX(A_i)+K]
[MAX(Ai),MAX(Ai)+K]。又发现所有灯都是
k
k
k分钟亮
k
k
k分钟暗,所以在答案区间内每个灯都是前
L
i
L_i
Li分钟亮灯后
K
−
L
i
K-L_i
K−Li分钟暗灯或者前
L
i
L_i
Li分钟暗灯后
K
−
L
i
K-L_i
K−Li分钟亮灯。我们只需要看每个
A
i
A_i
Ai在该区间起点是亮还是暗,还能亮/暗多久。对所有以亮灯开始的长度取最短亮灯时间,对所有暗灯开始的长度取最长暗灯时间,如果最短亮灯时间小于最长暗灯时间,那么操作就不合法,因为最长暗灯时间的那个灯与最短亮灯时间的那个灯在答案区间内至多有一个亮着。
如果操作合法,则最早全部亮灯时刻应该是最长暗灯长度+答案区间起点+1
如图,虚线框就是维护的最长暗灯长度和最短亮灯长度
c o d e : code: code:
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#define int long long
using namespace std;
const int N = 2e5+5;
int t;
int a[N];
int vis[N];
int lit[N];
int n,m,h,w;
signed main()
{
cin>>t;
while(t--)
{
int k;
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),vis[i]=0,lit[i]=0;
sort(a+1,a+1+n);
int flag=0;
int minl=0x3f3f3f3f;int maxd=0;
for(int i=1;i<=n;i++)
{
int cnt=(a[n]-a[i])/k;
int pos=a[i]+k*cnt;
if(cnt%2==1)
{
vis[i]=0;//an
lit[i]=pos+k-a[n];
maxd=max(maxd,lit[i]);
}
else
{
vis[i]=1;//liang
lit[i]=pos+k-a[n];
minl=min(minl,lit[i]);
}
if(minl<=maxd) flag=1;
}
if(flag)
{
printf("-1\n");
continue ;
}
else
{
if(maxd==0)
{
printf("%d\n",a[n]);
}
else
{
printf("%d\n",a[n]+maxd);
}
}
}
return 0;
}
题意:
数据范围: N , K < = 1 0 5 N,K<=10^5 N,K<=105
做法:首先,如果我们按模
k
k
k把下标分组,序列下标为0,1,2…k,0,1,2,…k…我们任意删去
k
k
k个数,该序列还是0,1,2…k,0,1,2,…k…,所以如果最终剩下
m
m
m个数,那他们的下标连起来一定是0,1,2,…m-1。
发现
A
i
>
=
c
n
t
的
A
i
的数量
A_i>=cnt的A_i的数量
Ai>=cnt的Ai的数量关于
c
n
t
cnt
cnt单调,所以我们可以二分最终序列的中位数。假设目前二分的答案为
c
n
t
cnt
cnt。
设
d
p
[
i
]
dp[i]
dp[i]为选到前
i
i
i个数时
>
=
c
n
t
>=cnt
>=cnt的数的个数,由于最终序列下标连续,所以
d
p
[
i
]
dp[i]
dp[i]要么是
d
p
[
i
−
k
]
dp[i-k]
dp[i−k]转移过来,要么是
d
p
[
i
−
1
]
dp[i-1]
dp[i−1]转移过来,特别注意由
d
p
[
i
−
k
]
dp[i-k]
dp[i−k]转移过来时不能再选
A
i
A_i
Ai了,因为
A
i
A_i
Ai与
A
i
−
k
A_i-k
Ai−k的下标均为
(
i
m
o
d
k
)
(i mod k)
(imodk)。而由
i
−
1
i-1
i−1转移过来是可以选
A
i
A_i
Ai的。那么
d
p
[
i
]
=
m
a
x
(
d
p
[
i
−
k
]
,
d
p
[
i
−
1
]
+
(
A
i
>
=
c
n
t
)
)
dp[i]=max(dp[i-k],dp[i-1]+(A_i >=cnt ))
dp[i]=max(dp[i−k],dp[i−1]+(Ai>=cnt))
又要注意如果 i m o d k = = 0 imodk==0 imodk==0,则 d p [ i ] dp[i] dp[i]只能从 d p [ i − k ] dp[i-k] dp[i−k]转移过来。答案序列也有可能是从 A i A_i Ai开始的,那么 d p [ i ] = m a x ( ( A i > = c n t , d p [ i − k ] ) ) dp[i]=max((A_i >=cnt,dp[i-k])) dp[i]=max((Ai>=cnt,dp[i−k]))
设最终剩余序列长度为
L
e
n
t
h
Lenth
Lenth,那么
d
p
[
n
]
>
=
L
e
n
t
h
/
2
+
1
dp[n]>=Lenth/2+1
dp[n]>=Lenth/2+1时,说明答案合法
c
o
d
e
:
code:
code:
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
const int N = 5e5+5;
int t;
int a[N];
int n,m,k,w;
int dp[N];
bool check(int x)
{
for(int i=0;i<=n;i++) dp[i]=0;
for(int i=1;i<=k;i++)
{
dp[i]=dp[i-1];
if(a[i]>=x) dp[i]++;
}
for(int i=k+1;i<=n;i++)
{
if(i%k==1)
{
if(a[i]>=x) dp[i]=1;
dp[i]=max(dp[i-k],dp[i]);
}
else
{
dp[i]=dp[i-1];
if(a[i]>=x) dp[i]++;
dp[i]=max(dp[i],dp[i-k]);
}
}
// for(int i=1;i<=n;i++) printf("i = %d dp = %d\n",i,dp[i]);
int maxnum=0;
// for(int i=1;i<=n;i++) maxnum=max(maxnum,dp[i]);
int cnt=(n-n/k*k);
if(cnt==0) cnt=k;
if(dp[n]>=(cnt)/2+1) return 1;
else return 0;
}
int main()
{
cin>>t;
while(t--)
{
scanf("%d%d",&n,&k);
int r=0;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),r=max(r,a[i]);
if(n<=k)
{
sort(a+1,a+1+n);
printf("%d\n",a[(n+1)/2]);
continue ;
}
int l=1;
int ans=0;
// printf("tot = %d\n",(n-(n/k)*k)/2+1);
while(l<=r)
{
int mid=(l+r)>>1;
// printf("l = %d r = %d mid = %d\n",l,r,mid);
if(check(mid))
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
printf("%d\n",r);
}
return 0;
}