A.
根据题目给的构造方式逆推出对应的下标。
前
(
n
+
1
)
/
2
(n+1)/2
(n+1)/2位为奇数位,后面的为偶数位,前后顺序相反。
代码:
#include <iostream>
#include <cstdio>
const int maxn=307;
using namespace std;
int n,T;
int a[maxn],b[maxn];
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
int k=(n+1)/2;
for (int i=1;i<=k;i++) b[i*2-1]=a[i];
for (int i=k+1;i<=n;i++) b[(n-i+1)*2]=a[i];
for (int i=1;i<=n;i++) printf("%d ",b[i]);
printf("\n");
}
}
B.
显然需要存在一个前缀与一个后缀连起来等于字符
“
2020
”
“2020”
“2020”即可。
代码:
#include <iostream>
#include <cstdio>
const int maxn=207;
using namespace std;
int n,T,flag;
char s[maxn],p[maxn];
int main()
{
scanf("%d",&T);
while (T--)
{
flag=0;
scanf("%d",&n);
scanf("%s",s+1);
for (int i=0;i<=4;i++)
{
for (int k=1;k<=i;k++) p[k]=s[k];
for (int k=1;k<=4-i;k++) p[i+k]=s[n-(4-i)+k];
if ((p[1]=='2') && (p[2]=='0') && (p[3]=='2') && (p[4]=='0'))
{
printf("YES\n");
flag=1;
break;
}
if (flag) break;
}
if (!flag) printf("NO\n");
}
}
C.
显然位数越小越优,位数相等首位最小最优。所以我们从9到1反着取数,如果加上某个
i
i
i后大于等于
x
x
x,那么把这个
i
i
i删去,换成
x
−
s
u
m
i
x-sumi
x−sumi即可,然后从小到大输出。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int n,T,p;
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
int i;
p=0;
for (i=9;i>0;i--)
{
p+=i;
if (p>=n) break;
}
if (p<n) printf("-1\n");
else
{
p-=i;
printf("%d",n-p);
for (int j=i+1;j<=9;j++) printf("%d",j);
printf("\n");
}
}
}
D.
因为只能合并相邻的数,所以问题相当于把数列分成若干段,每段的数的和相等。
我们可以枚举最后每段的值是多少,设
s
u
m
=
∑
i
=
1
n
a
i
sum=\sum_{i=1}^{n}a_i
sum=∑i=1nai,枚举
s
u
m
sum
sum的因数
x
x
x,然后判断是否可以使得每一段和都为
x
x
x即可。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
const int maxn=3007;
using namespace std;
int n,T,sum,ans;
int a[maxn];
bool check(int lim)
{
int p=0;
for (int i=1;i<=n;i++)
{
p+=a[i];
if (p>lim) return 0;
if (p==lim) p=0;
}
return 1;
}
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
sum=0;
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];
}
ans=n;
int k=trunc(sqrt(sum));
for (int i=1;i<=k;i++)
{
if (sum%i==0)
{
if (check(i)) ans=min(ans,n-sum/i);
if (check(sum/i)) ans=min(ans,n-i);
}
}
printf("%d\n",ans);
}
}
E.
考虑枚举最小值,并记录每个值的数的个数,并预处理出前缀和
s
u
m
i
sum_i
sumi。
那么以
x
x
x为最小值的合法答案为
(
s
u
m
x
+
k
−
s
u
m
x
−
1
m
)
−
(
s
u
m
x
+
k
−
s
u
m
x
m
)
\binom{sum_{x+k}-sum_{x-1}}{m}-\binom{sum_{x+k}-sum_{x}}{m}
(msumx+k−sumx−1)−(msumx+k−sumx)。
这样就保证了最小值为
x
x
x,就不会算重。
E1因为确定了
m
m
m和
k
k
k,但不能取模,直接把组合数拆开即可。
E2预处理阶乘与逆元即可。
代码:
E1(用上面的公式即可,不需要像代码一样分类讨论).
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
const int maxn=2e5+7;
using namespace std;
int n,T,x;
int a[maxn];
LL ans;
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
for (int i=1;i<=n;i++) a[i]=0;
for (int i=1;i<=n;i++)
{
scanf("%d",&x);
a[x]++;
}
ans=0;
for (int i=1;i<=n;i++)
{
if (a[i]>=3) ans+=(LL)a[i]*(LL)(a[i]-1)*(LL)(a[i]-2)/6;
if ((i<n) && (a[i]>1) && (a[i+1]>=1)) ans+=(LL)a[i]*(LL)(a[i]-1)*(LL)a[i+1]/2;
if ((i<n) && (a[i]>=1) && (a[i+1]>1)) ans+=(LL)a[i]*(LL)(a[i+1]-1)*(LL)a[i+1]/2;
if ((i<n-1) && (a[i]>1) && (a[i+2]>=1)) ans+=(LL)a[i]*(LL)(a[i]-1)*(LL)a[i+2]/2;
if ((i<n-1) && (a[i]>=1) && (a[i+2]>1)) ans+=(LL)a[i]*(LL)(a[i+2]-1)*(LL)a[i+2]/2;
if ((i<n-1) && (a[i]>=1) && (a[i+1]>=1) && (a[i+2]>=1)) ans+=(LL)a[i]*(LL)a[i+1]*(LL)a[i+2];
}
printf("%lld\n",ans);
}
}
E2.
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
const int maxn=2e5+7;
const LL mod=1e9+7;
using namespace std;
int n,T,x,m,k;
int a[maxn],sum[maxn];
LL jc[maxn],inv[maxn];
LL ans;
LL ksm(LL x,LL y)
{
if (y==1) return x;
LL c=ksm(x,y/2);
c=(c*c)%mod;
if (y&1) c=(c*x)%mod;
return c;
}
LL C(int n,int m)
{
if (n<m) return 0;
return jc[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d%d%d",&n,&m,&k);
for (int i=1;i<=n;i++) a[i]=0;
for (int i=1;i<=n;i++)
{
scanf("%d",&x);
a[x]++;
}
jc[0]=1;
for (int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+a[i];
jc[i]=jc[i-1]*(LL)i%mod;
}
inv[n]=ksm(jc[n],mod-2);
for (int i=n-1;i>=0;i--) inv[i]=inv[i+1]*(LL)(i+1)%mod;
ans=0;
int p;
for (int i=1;i<=n;i++)
{
if (i+k>n) p=n;
else p=i+k;
if (sum[p]-sum[i-1]<m) continue;
ans=(ans+C(sum[p]-sum[i-1],m)+mod-C(sum[p]-sum[i],m))%mod;
}
printf("%lld\n",ans);
}
}
F.
我们可以枚举一个区间,再把与他相交的区间加上,那么这个集合一定是合法的。
我们可以先把区间离散化,并把区间按左边界排序,维护两个指针
l
l
l与
r
r
r,维护
s
u
m
sum
sum表示与
[
l
,
r
]
[l,r]
[l,r]相交的区间的个数。如果
r
r
r遇到一个左边界,则
s
u
m
+
1
sum+1
sum+1;如果
l
l
l过了一个有边界,则
s
u
m
−
1
sum-1
sum−1。因为我们以及对区间左边界排序,而后面区间右边界如果小于前面区间的右边界,那么后面的区间就被前面的区间包含,他一定不是最优的。所以可以保证
l
,
r
l,r
l,r均单调,维护的复杂度就是
O
(
n
)
O(n)
O(n) 。当然总复杂度是排序的
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
const int maxn=4e5+7;
using namespace std;
int n,T,l,r,sum,ans;
int b[maxn],p[maxn],q[maxn];
struct rec{
int x,y;
}a[maxn];
bool cmp(rec a,rec b)
{
if (a.x==b.x) return a.y>b.y;
return a.x<b.x;
}
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
b[i*2-1]=a[i].x;
b[i*2]=a[i].y;
}
sort(b+1,b+n*2+1);
int len=unique(b+1,b+2*n+1)-b;
for (int i=1;i<=2*n;i++) p[i]=q[i]=0;
for(int i=1;i<=n;i++)
{
a[i].x=lower_bound(b+1,b+len,a[i].x)-b;
a[i].y=lower_bound(b+1,b+len,a[i].y)-b;
p[a[i].x]++;
q[a[i].y]++;
}
sort(a+1,a+n+1,cmp);
l=r=sum=ans=0;
for (int i=1;i<=n;i++)
{
if ((l<=a[i].x) && (a[i].y<=r)) continue;
while (r<a[i].y) r++,sum+=p[r];
while (l<a[i].x) sum-=q[l],l++;
ans=max(ans,sum);
}
printf("%d\n",n-ans);
}
}