总算是正常的普及组模拟赛难度了。。昨天的甚至算不上普及组模拟赛难度吧(

A

二分答案,我们肯定是贪心等到必须要选取某个点的时候再选取。

可以每次二分判断,也可以开个指针维护中位数在哪里来判断。

#include <bits/stdc++.h>

#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 1e5 + 5;
int n,k;LL a[MAXN];
int f[MAXN];

inline int get(int l,int r){
    if(l+1 >= r) return 0;
    LL mid = (a[l] + a[r]) >> 1;
    int p = std::lower_bound(a+l,a+r+1,mid)-a;
    LL ans = 0;
    FOR(i,p-1,p+1) if(i >= l && i <= r) ans = std::max(ans,std::min(a[i]-a[l],a[r]-a[i]));
    return ans;
}

inline bool chk(LL k){
    f[0] = 0;f[1] = 1;
    int p = 1;
    FOR(i,2,n){
        if(a[i]-a[1] <= k) f[i] = 1;
        else f[i] = 1e9;
        while(p <= i && get(p,i) > k) ++p;
        f[i] = std::min(f[i],f[p]+1);
    }
    int ans = 1e9;
    ROF(i,n,1){
        if(a[n]-a[i] <= k) ans = std::min(ans,f[i]);
    }
    return ans <= ::k;
}

int main(){
    scanf("%d%d",&n,&k);
    FOR(i,1,n) scanf("%lld",a+i);
    LL l = 0,r = 2e9,ans = -1;
    while(l <= r){
        LL mid = (l+r)>>1;
        if(chk(mid)) ans = mid,r = mid-1;
        else l = mid+1;
    }
    printf("%lld\n",ans);
    return 0;
}

B

模拟一下 $n=6$ 的过程:

1 2 3 4 5 6
2 1 4 3 6 5
1 4 2 6 5 3
4 2 6 1 3 5
2 6 1 3 4 5
6 1 3 4 5 2

看起来毫无规律,但是我们观察发现每次移动后在每一组开头的数都会移动,其他的数单调减一,会空出来若干个空,我们可以将这些数直接填到空里。

这种题我们考虑向右移动一位,那么其他数就是不变。

1 2 3 4 5 6
  2 1 4 3 6 5
    1 4 2 6 5 3
      4 2 6 1 3 5
        2 6 1 3 4 5
          6 1 3 4 5 2

发现相当于不同的数都往后移动了一位,直接模拟即可。

现在有一种形象的理解:每次将移动的位置全都往后统一移动。

[1] 2 [3] 4 [5] 6
    2 [1] 4 [3] 6 [5]
#include <bits/stdc++.h>

#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 1e6 + 5;
int n;
int a[MAXN<<1];

int main(){
    scanf("%d",&n);
    FOR(i,1,n) a[i] = i;
    FOR(i,2,n){
        int t = 0;
        for(int j = i-1;j <= i-1+n-1;j += i){
            std::swap(t,a[j]);
        }
        std::swap(t,a[i-1+n]);
    }
    FOR(i,n,n+n-1) printf("%d ",a[i]);puts("");
    return 0;
}

C

设 $f_{i,j}$ 表示考虑了前 $i$ 个位置,最后一次选在 $i$ 上,选了 $j$ 个灯的方案数。预处理一下 $g_{i,j}$ 表示选了 $i,j$ 但是没有选中间的灯的代价,就可以 $O(n^2k)$ 转移了。

据说这个东西满足四边形不等式,所以可以决策单调性优化。考试的时候比较懒,没有预处理,因为常数很小所以效果很好。

#include <bits/stdc++.h>

#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 200+5;
const int MAXM = 30 +5;
int n,k;
LL f[MAXN][MAXM];
LL a[MAXN];
LL suf[MAXN];

int main(){
    scanf("%d%d",&n,&k);
    FOR(i,1,n) scanf("%lld",a+i);a[0] = -1e18;
    ROF(i,n,1) suf[i] = suf[i+1]+a[i];
    CLR(f,0x3f);
    f[0][0] = 0;
    FOR(i,1,n){
        FOR(j,1,std::min(i,k)){
            FOR(k,0,i-1){
                LL gx = 0;
                FOR(l,k+1,i-1){
                    gx += std::min(a[l]-a[k],a[i]-a[l]);
                }
                f[i][j] = std::min(f[i][j],f[k][j-1]+gx);
            }
        }
    }
    LL ans = 1e18;
    FOR(i,1,n){
        ans = std::min(ans,f[i][k]+suf[i+1]-(n-i)*a[i]);
    }
    printf("%lld\n",ans);
    return 0;
}

D

发现所有的组合一定满足形如一个中心点连出去三条链。

所以对于每个点预处理出距离这个点为 $2$ 的点的点权和,平方和,立方和,就可以容斥求出答案。

sb出题人造的"树"让我少了 $40$ + 调了一中午。。原因是他们写的是 dfs 只算了一个连通块。。

#include <bits/stdc++.h>

#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 1e5 + 5;
LL a[MAXN];int n;

struct Edge{
    int to,nxt;
}e[MAXN<<1];
int head[MAXN],cnt;

inline void add(int u,int v){
    e[++cnt] = (Edge){v,head[u]};head[u] = cnt;
    e[++cnt] = (Edge){u,head[v]};head[v] = cnt;
}

LL f[MAXN][4],g[MAXN][4];

inline LL calc(LL s1,LL s2,LL s3){
    return s1*s1*s1-s2*s1*3+s3*2;
}

inline LL calc2(LL s1,LL s2){
    return s1*s1-s2;
}

int main(){
    scanf("%d",&n);
    FOR(i,1,n) scanf("%lld",a+i);
    FOR(i,2,n){
        int u,v;scanf("%d%d",&u,&v);
        add(u,v);
    }
    FOR(v,1,n) for(int i = head[v];i;i = e[i].nxt){
        LL val = a[e[i].to];
        FOR(j,1,3){
            f[v][j] += val;
            val = val*a[e[i].to];
        }
    }
    FOR(v,1,n) for(int i = head[v];i;i = e[i].nxt){
        g[v][1] += f[e[i].to][1]-a[v];
        g[v][2] += f[e[i].to][2]-a[v]*a[v];
        g[v][3] += f[e[i].to][3]-a[v]*a[v]*a[v];
    }
    LL ans = 0;
    FOR(i,1,n) ans += calc(g[i][1],g[i][2],g[i][3]);//,DEBUG(calc(g[i][1],g[i][2],g[i][3]));
    FOR(v,1,n){
        LL gx = 0,sm = 0;
        for(int i = head[v];i;i = e[i].nxt) sm += f[e[i].to][1]-a[v];
        for(int i = head[v];i;i = e[i].nxt){
            gx += calc(f[e[i].to][1]-a[v],f[e[i].to][2]-a[v]*a[v],f[e[i].to][3]-a[v]*a[v]*a[v]);
            ans -= calc(f[e[i].to][1]-a[v],f[e[i].to][2]-a[v]*a[v],f[e[i].to][3]-a[v]*a[v]*a[v]);
            LL t = calc2(f[e[i].to][1]-a[v],f[e[i].to][2]-a[v]*a[v]);
            sm -= f[e[i].to][1]-a[v];
            ans -= t*sm*3;
            sm += f[e[i].to][1]-a[v];
        }
//        DEBUG(gx);
    }
//    assert(ans%6==0);
    ans /= 6;
    printf("%lld\n",ans);
    return 0;
}
Last modification:October 2nd, 2020 at 04:32 pm
如果觉得我的文章对你有用,请随意赞赏