总算是正常的普及组模拟赛难度了。。昨天的甚至算不上普及组模拟赛难度吧(
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;
}
2 comments
RainAir yyds!
Orz RainAir