文章目录
A. Cubes Sorting
判断逆序数个数是否大于n*(n-1)/2,需要时间复杂度O(nlogn)。归并排序或者树状数组都可,这里是归并排序。
#include <iostream>
#include <stdio.h>
using namespace std;
const int maxn=1e5+5;
int a[maxn];//原数组
int b[maxn];//辅助数组
long long ans;
void merge(int l,int r,int *c)//合并
{
int mid=(l+r)>>1;
int i=l,j=mid+1,cnt=l;
while(i<=mid&&j<=r)
{
//小的放
if(c[i]>c[j])
{
b[cnt++]=c[j++];
ans+=mid-i+1;
}
else
b[cnt++]=c[i++];
}
//把没比较的直接放
while(i<=mid)
b[cnt++]=c[i++];
while(j<=r)
b[cnt++]=c[j++];
for(int k=l;k<=r;k++)//更新a数组
c[k]=b[k];
}
void mergesort(int l,int r,int *c)
{
if(l==r)
return;
int mid=(l+r)>>1;
mergesort(l,mid,c);
mergesort(mid+1,r,c);
merge(l,r,c);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
ans=0;
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
mergesort(1,n,a);
if(1ll*n*(n-1)/2-1<ans)
puts("NO");
else
puts("YES");
}
return 0;
}
B. Rock and Lever
我们发现,如果两个数最高位是在同一位,那么这两个就能贡献一组,所以,排序一下,线性扫一下更新答案即可。
#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int T,n;
int a[N];
int get(int x)
{
int cnt=0;
while(x)
{
x>>=1;
cnt++;
}
return cnt;
}
int main()
{
scanf("%d",&T);
//cout<<get(8)<<endl;
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
sort(a+1,a+1+n);
int l=1,r=1;
long long ans=0;
while(r<=n)
{
if(get(a[l])<get(a[r]))
{
ll tmp=1ll*(r-l);
ans+=tmp*(tmp-1)/2;
l=r;
}
else
{
r++;
}
}
ll tmp=1ll*(r-l);
ans+=tmp*(tmp-1)/2;
printf("%lld\n",ans);
}
return 0;
}
C1. Pokémon Army (easy version)
我用dp过的(而且状态也写得很复杂),这里也简单说一下,dp[i][0]表示以i结尾选择i未完成匹配的最大值,dp[i][1]表示以i结尾选择i完成匹配的最大值,dp[i]][2]以i结尾不选i的最大值。
状态转移方程如下。
我们可以维护一个ma,为前i-1个dp[j][0]的最大值,就可以O(1)的更新dp[i][1]了。
#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll inf=(1ll<<50);
const int N=3e5+5;
ll dp[N][3];
int T,n,q;
ll a[N];
int main()
{
while(~scanf("%d",&T))
{
while(T--)
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i)
{
scanf("%lld",&a[i]);
for(int j=0;j<3;++j)
dp[i][j]=0;
}
ll ma=-inf;
for(int i=1;i<=n;++i)
{
dp[i][0]=max(dp[i-1][1],dp[i-1][2])+a[i];
dp[i][1]=ma-a[i];
ma=max(ma,dp[i][0]);
dp[i][2]=max(dp[i-1][1],dp[i-1][2]);
//cout<<i<<" "<<dp[i][0]<<" "<<dp[i][1]<<" "<<dp[i][2]<<endl;
}
ma=-inf;
for(int i=1;i<=n;++i)
ma=max(ma,max(dp[i][0],max(dp[i][1],dp[i][2])));
printf("%lld\n",ma);
}
}
return 0;
}
C2. Pokémon Army (hard version)
其实这题不用dp,可以贪心的做。来看下图。
如果是这种情况,我们选加颜色的点为最优解。给出一个简单的证明,来看左边单调递减的部分。
这四个点记为a1,a2,a3,a4。其大小关系如下。
如果我们顺序选 a1-a2+a3-a4。不如选a1-a4。因为a1-a4+a3-a2<a1-a4(a3-a2<0)。其它情况显而也不如a1-a4。所以选峰和底是最好的。
hard版本,我们发现每次交换,只会改变自己,和左右两个位置,所以每次O(1)模拟改变一下即可。
#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
int T,n,q,l,r;
const ll N=3e5+5;
ll ans;
ll a[N];
void insert(int x)
{
if(x<=0||x>n)
return ;
if(a[x-1]<a[x]&&a[x]>a[x+1])
ans+=a[x];
if(a[x-1]>a[x]&&a[x]<a[x+1])
ans-=a[x];
}
void delet(int x)
{
if(x<=0||x>n)
return;
if(a[x-1]<a[x]&&a[x]>a[x+1])
ans-=a[x];
if(a[x-1]>a[x]&&a[x]<a[x+1])
ans+=a[x];
}
int main()
{
while(~scanf("%d",&T))
{
while(T--)
{
ans=0;
scanf("%d%d",&n,&q);
a[0]=a[n+1]=-1;
for(int i=1;i<=n;++i)
{
scanf("%lld",&a[i]);
//insert(i);
}
for(int i=1;i<=n;++i)
insert(i);
printf("%lld\n",ans);
while(q--)
{
scanf("%d%d",&l,&r);
/*if(l==r)
{
printf("%lld\n",ans);
continue;
}
*/
delet(l-1);
delet(l);
delet(l+1);
if(r-1>l+1)
delet(r-1);
if(r>l+1)
delet(r);
if(r+1>l+1)
delet(r+1);
swap(a[l],a[r]);
insert(l-1);
insert(l);
insert(l+1);
if(r-1>l+1)
insert(r-1);
if(r>l+1)
insert(r);
if(r+1>l+1)
insert(r+1);
printf("%lld\n",ans);
}
}
}
return 0;
}
D. Rescue Nibel!
我们发现区间有1e9,但是n只有3e5。显然需要离散化,离散之后,遍历所有时间,记录每个时间点多了几个灯,少了几个灯。记录现在还有几个灯sum,遍历时间节点,对于每个时间节点,ans+=C(sum+in[i]-out[i],k)-C(sum-out[i],k)。减去的是之前算过的重复方案。组合数可以通过预处理O(1)的去计算。
#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=3e5+5;
const ll mod=998244353;
int n,cnt;
ll k;
int l[N],r[N];
int C[N<<1];
int in[N<<1],out[N<<1];
ll A[N<<1],inv[N<<1];
long long quickpow(long long x,long long k)
{
long long res=1;
while(k)
{
if(k&1)//k为奇数
res=res*x%mod;
k>>=1;//k/2
x=x*x%mod;
}
return res;
}
long long getC(ll n,ll m)
{
//A[]为阶乘,inv[]为阶乘倒数
if(n==m||!m)
return 1;
else
return A[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
//cout<<getC()
scanf("%d%lld",&n,&k);
//A[]为阶乘,inv[]为阶乘倒数
A[0]=1;//0!=1
for(int i=1;i<=n;i++)//n!=(n-1)!*n
A[i]=A[i-1]*i%mod;
//费马小定理
inv[n]=quickpow(A[n],mod-2);//quickpow快速幂
for(int i=n-1;i>=0;i--)//1/n!*n=1/(n-1)!
inv[i]=inv[i+1]*(i+1)%mod;
//cout<<inv[1]<<endl;
//cout<<A[4]<<" "<<inv[3]<<" "<<getC(4,2)<<endl;
for(int i=1;i<=n;++i)
{
scanf("%d%d",&l[i],&r[i]);
C[++cnt]=l[i];
C[++cnt]=r[i];
}
sort(C+1,C+1+cnt);
int siz=unique(C+1,C+1+cnt)-C-1;
ll sum=0;
for(int i=1;i<=n;++i)
{
int pos=lower_bound(C+1,C+1+siz,l[i])-C;
in[pos]++;
pos=lower_bound(C+1,C+1+siz,r[i])-C;
out[pos+1]++;
}
ll ans=0;
for(int i=1;i<=siz;++i)
{
sum-=(long long)out[i];
ll t1=0;
if(sum>=k)
t1=getC(sum,k);
ll t2=0;
sum+=(long long)in[i];
if(sum>=k)
t2=getC(sum,k);
ans=(ans+t2-t1+mod)%mod;
//cout<<in[i]<<" "<<out[i]<<" "<<sum<<" "<<t1<<" "<<t2<<endl;
}
printf("%lld\n",ans);
return 0;
}