本应$280$的,结果打表太大,拒绝评测,只有$180$。
Problem A 旅行
题面
现在有一段不平整的路,第$i$段的高度为$a_i$。你可以花费$|a_i-k|$,使第$i$段的高度变为$k$,使$\sum_{i=1}^{n-1}|a_i-a_{i+1}|$最小。
题解
贪心地从左到右扫一遍,枚举第$i$段路。如果要使得从$i-1$到$i+1$的体力消耗最小,那么就要两端距离间的差距最小。
- 如果$a_{i-1}$和$a_{i+1}$都小于$a_i$,那么就将$a_i$改变为$max(a_{i-1},a_{i+1})$。
- 如果$a_{i-1}$和$a_{i+1}$都大于$a_i$,那么就将$a_i$改变为$min(a_{i-1},a_{i+1})$。
代码
#include
#include
#include
using namespace std;
const int MAXN=100005;
int main(){
#ifndef FILE_IN
freopen("travel.in","r",stdin);
#endif
#ifndef FILE_OUT
freopen("travel.out","w",stdout);
#endif
ios::sync_with_stdio(false);
int n;
int a[MAXN]={0};
long long ans=0;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=2;ia[i]&&a[i]a[i+1]){
ans+=a[i+1]-a[i];
a[i]=a[i+1];
}
else{
ans+=a[i-1]-a[i];
a[i]=a[i-1];
}
}
else if(a[i-1]a[i+1]){
if(a[i-1]>a[i+1]){
ans+=a[i]-a[i-1];
a[i]=a[i-1];
}
else{
ans+=a[i]-a[i+1];
a[i]=a[i+1];
}
}
}
for(int i=1;i
Problem B 石头剪刀布
题面
小菜给出一个自然数$n$,小明和小头轮流操作,每次操作可以把$n$减去$n$所拥有的数字中的最大值或者最小值(不包括0),不能操作者输(即$n=0$ 时)。
试求双方最优策略时的胜负情况。(小明先手)
题解
递推。$$fuck_x=(!fuck_{x-findmax(x)})||(!fuck_{x-findmin(x)})$$
代码
#include
#include
using namespace std;
const int MAXN=1000005;
bool fuck[MAXN]={0,1,1,1,1,1,1,1,1,1};
short findmin(int x){
short ans;
do{
ans=x%10;
x/=10;
}
while(x!=0&&ans==0);
while(x){
if(x%100){
ans=x%10;
}
x/=10;
}
return ans;
}
short findmax(int x){
short ans;
do{
ans=x%10;
x/=10;
}
while(x!=0&&ans==0);
while(x){
if(x%10>ans&&(x%10)){
ans=x%10;
}
x/=10;
}
return ans;
}
void dfs(int x){
fuck[x]=(!fuck[x-findmax(x)])||(!fuck[x-findmin(x)]);
}
int main(){
ios::sync_with_stdio(false);
for(int i=10;i>n;
for(int i=1;i<=n;i++){
int q;
cin>>q;
if(fuck[q]){
cout<<"YES"<
Problem C 环中环
题面
$n$个整数围成一个环,选出其中的若干数,使得选中的数所组成的环中,两个相邻数的差的绝对值不等于$1$。在满足这个前提下,问最多能取多少个数。
题解
动态规划。设$f_i$表示以$i$为终点时最多能取多少个数。$$f_i=f_j+1(abs( a_i-a_j) \neq 1)$$
由于题目要求是环形,因此可以将原数列复制一份接在后面,处理完后答案除以$2$即可。
我们发现$(abs( a_ i-a_ j ) \neq 1)$的数最多只有两个,所以我们只要记录$3$个不同的$a_i$所对应的最大的$3$个$f$的值,直接转移即可。
代码
#include
#include
#include
using namespace std;
int n, a[200001], f[200001], tr[5000000], zl;
void find( int z, int x, int y, int l, int r ) {
if ( ( x == l ) && ( y == r ) )
zl = max( zl, tr[z] );
else {
int mid = ( x + y ) / 2;
if ( r <= mid )
find( z * 2, x, mid, l, r );
else {
if ( l > mid )
find( z * 2 + 1, mid + 1, y, l, r );
else {
find( z * 2, x, mid, l, mid );
find( z * 2 + 1, mid + 1, y, mid + 1, r );
}
}
}
}
int findx( int x, int y ) {
zl = 0;
if ( x >= 0 && y >= 0 )
find( 1, 0, 1100000, x, y );
else
return ( 0 );
return ( zl );
}
void change( int z, int x, int y, int l ) {
if ( x == y )
tr[z] = zl;
else {
int mid = ( x + y ) / 2;
if ( l <= mid )
change( z * 2, x, mid, l );
else
change( z * 2 + 1, mid + 1, y, l );
tr[z] = max( tr[z * 2], tr[z * 2 + 1] );
}
}
int main() {
ios::sync_with_stdio( false );
cin >> n;
int i, j, k;
for ( int i = 1; i <= n; i++ ) {
cin >> a[i];
a[i + n] = a[i];
}
memset( tr, 0, sizeof( tr ) );
for ( int i = 1; i <= 2 * n; i++ ) {
f[i] = max( findx( a[i] + 2, 1100000 ), max( findx( 0, a[i] - 2 ), findx( a[i], a[i] ) ) );
zl = f[i] + 1;
f[i] += 1;
change( 1, 0, 1100000, a[i] );
}
cout << ( tr[1] >> 1 ) << endl;
return 0;
}
Problem D 寻找神格
题面
维护一个序列,使之支持下列操作:
- 一个区间增加一个定值。
- 求指定区间方差。
- 求指定区间和。
题解
分块。
代码
#include
#include
#include
using namespace std;
typedef long long ll;
typedef long double ld;
const int N=100010;
const int M=350;
int n,Q,T,L[M],R[M],pos[N];
ll ans[M],add[M],sum[M],a[N],Read,f,x,y,z;
char ch;
ll read(){
Read=0;f=1;ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-f;ch=getchar();}
while (ch>='0'&&ch<='9')Read=(Read<<3)+(Read<<1)+ch-48,ch=getchar();
return Read*f;
}
void change(int l,int r,ll z) //修改
{
int q=pos[l],p=pos[r];
if (q==p) //一个块暴力修改
{
for (register int i=l;i<=r;i++)
ans[q]+=2*(a[i]+add[q])*z+z*z,a[i]+=z,sum[q]+=z;
return;
}
for (register int i=l;i<=R[q];i++)
ans[q]+=2*(a[i]+add[q])*z+z*z,a[i]+=z,sum[q]+=z;
for (register int i=L[p];i<=r;i++) //分开
ans[p]+=2*(a[i]+add[p])*z+z*z,a[i]+=z,sum[p]+=z;
for (register int i=q+1;i
此处评论已关闭