号家军集训 杂题选做

讲师:罗恺

CF354D

题目简介

题目名称:

题目来源:

评测链接:https://codeforces.com/problemset/problem/354/D

形式化题意:有一个高度为 的金字塔(即第 行只有 列的矩形),其中存在 个黑点,你有两种操作:

  • 将某一个点染白,代价为
  • 将以某一个点为顶点的子金字塔染白,代价为点数

求将所有黑点染白的最小代价。

数据范围:

考虑在特定情况下哪一个操作会更优。设当前操作的点为 ,其为顶点的子金字塔共有 个白点,那么两个代价分别是 ,因为 ,很显然 是答案的上界,如果存在 的话,是肯定不优的,所以 级的。

然后考虑设 表示前 列中,第 个是被操作二删除的最小代价,这意味着 都是由操作一删除的,贡献为点数 ,而后面的转移,就可以依次枚举 进行转移,这样是 的。

然后你反过来想就可以优化一维:

最后复杂度就是 的了。

AC Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// ----- Eternally question-----
// Problem: D. Transferring Pyramid
// Contest: Codeforces - Codeforces Round 206 (Div. 1)
// URL: https://codeforces.com/problemset/problem/354/D
// Memory Limit: 256 MB
// Time Limit: 3000 ms
// Written by: Eternity
// Time: 2023-07-21 14:46:17
// ----- Endless solution-------

#include<bits/stdc++.h>
#define re register
typedef long long ll;
template<class T>
inline void read(T &x)
{
x=0;
char ch=getchar(),t=0;
while(ch<'0'||ch>'9') t|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
if(t) x=-x;
}
template<class T,class ...T1>
inline void read(T &x,T1 &...x1){ read(x),read(x1...); }
template<class T>
inline void write(T x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
template<>
inline void write(bool x){ std::cout<<x; }
template<>
inline void write(char c){ putchar(c); }
template<>
inline void write(char *s){ while(*s!='\0') putchar(*s++); }
template<>
inline void write(const char *s){ while(*s!='\0') putchar(*s++); }
template<class T,class ...T1>
inline void write(T x,T1 ...x1){ write(x),write(x1...); }
template<class T>
inline bool checkMax(T &x,T y){ return x<y?x=y,1:0; }
template<class T>
inline bool checkMin(T &x,T y){ return x>y?x=y,1:0; }
const int MAXN=1e5+10,MAXS=1e3+10;
const int INF=0x3f3f3f3f;
using Pir=std::pair<int,int>;
#define fir first
#define sec second
int N,K;
int Dp[2][MAXS];
std::vector<int>Pos[MAXN];
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(N,K);
int lim=std::min(N,780);
for(int i=1,x,y;i<=K;++i)
{
read(x,y);
Pos[y].emplace_back(N-x+1);
}
for(int i=1;i<=N;++i) std::sort(Pos[i].begin(),Pos[i].end());
std::memset(Dp,0x3f,sizeof(Dp));
Dp[0][0]=0;
for(int i=1;i<=N;++i)
{
int f=(i-1)&1,s=i&1;
std::memset(Dp[s],0x3f,sizeof(Dp[s]));
int k=0,sz=Pos[i].size();
Dp[s][0]=Dp[f][0]+(sz-k)*3;
for(int j=0;j+1<=lim&&j<=N-i+1;++j)
{
while(k<sz&&Pos[i][k]<=j) ++k;
checkMin(Dp[s][j],Dp[f][j+1]+(sz-k)*3);
}
k=0;
int mn=std::min(Dp[f][0],Dp[f][1]);
for(int j=1;j<=N-i+1&&j<=lim;++j)
{
while(k<sz&&Pos[i][k]<=j) ++k;
if(j+1<=lim) checkMin(mn,Dp[f][j+1]);
checkMin(Dp[s][j],mn+j*(j+1)/2+2+(sz-k)*3);
}
}
write(std::min(Dp[N&1][0],Dp[N&1][1]));
return 0;
}

CF677D

题目简介

题目名称:

题目来源:

评测链接:https://codeforces.com/problemset/problem/677/D

形式化题意:在一个 的网格上,每个点有一个数字 ,起点在 ,场上有且仅有一个 ,求一条最短的路径,要求终点在 处,且经过的路径序列中存在一个子序列为

数据范围:

考虑分层图来做,我们用所有 的结点来更新 结点,但这样很显然会被卡,考虑一组数据是 ,然后 个,最后时间复杂度还是会变成

然后考虑如果不跑最短路的话,最后时间复杂度就是 ,但这样也很显然会被卡,考虑一组数据是 ,然后 一半是 ,一半是 ,这样的复杂度高达 ,不可接受。

然后发现两个方法被卡掉的数据正好是两个极端,那我们就正好平衡规划一下,考虑当 时,我们进行 ,否则直接枚举,这样下来的时间复杂度平摊 ,可以通过。

注意在于 ,用双端队列维护所有起点从小到大加入即可,好久没写搜索手生了。

AC Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// ----- Eternally question-----
// Problem: D. Vanya and Treasure
// Contest: Codeforces - Codeforces Round 355 (Div. 2)
// URL: https://codeforces.com/problemset/problem/677/D
// Memory Limit: 256 MB
// Time Limit: 1500 ms
// Written by: Eternity
// Time: 2023-07-20 19:15:55
// ----- Endless solution-------

#include<bits/stdc++.h>
#define re register
typedef long long ll;
template<class T>
inline void read(T &x)
{
x=0;
char ch=getchar(),t=0;
while(ch<'0'||ch>'9') t|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
if(t) x=-x;
}
template<class T,class ...T1>
inline void read(T &x,T1 &...x1){ read(x),read(x1...); }
template<class T>
inline void write(T x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
template<>
inline void write(bool x){ std::cout<<x; }
template<>
inline void write(char c){ putchar(c); }
template<>
inline void write(char *s){ while(*s!='\0') putchar(*s++); }
template<>
inline void write(const char *s){ while(*s!='\0') putchar(*s++); }
template<class T,class ...T1>
inline void write(T x,T1 ...x1){ write(x),write(x1...); }
template<class T>
inline bool checkMax(T &x,T y){ return x<y?x=y,1:0; }
template<class T>
inline bool checkMin(T &x,T y){ return x>y?x=y,1:0; }
using Pir=std::pair<int,int>;
#define fir first
#define sec second
const int MAXN=3e2+10,MAXM=9e4+10;
const int INF=0x3f3f3f3f;
const int dx[]={0,1,-1,0,0};
const int dy[]={0,0,0,1,-1};
int N,M,P;
std::vector<Pir>vec[MAXM];
int a[MAXN][MAXN],Dist[MAXN][MAXN][2],fr,to;
inline int getDist(Pir a,Pir b){ return std::abs(a.fir-b.fir)+std::abs(a.sec-b.sec); }
inline bool cmp(Pir a,Pir b){ return Dist[a.fir][a.sec][fr]<Dist[b.fir][b.sec][fr]; }
inline void calcCount(int s)
{
fr=(s-1)&1,to=s&1;
for(auto v:vec[s]) Dist[v.fir][v.sec][to]=INF;
for(auto x:vec[s-1]) for(auto v:vec[s]) checkMin(Dist[v.fir][v.sec][to],Dist[x.fir][x.sec][fr]+getDist(x,v));
}
bool Vis[MAXN][MAXN];
inline int dis(Pir a){ return Dist[a.fir][a.sec][to]; }
inline void calcBfs(int s)
{
fr=(s-1)&1,to=s&1;
for(int i=1;i<=N;++i)
for(int j=1;j<=M;++j)
Dist[i][j][to]=INF,Vis[i][j]=0;
std::sort(vec[s-1].begin(),vec[s-1].end(),cmp);
for(auto x:vec[s-1]) Dist[x.fir][x.sec][to]=Dist[x.fir][x.sec][fr];
std::deque<Pir>Q;
Q.push_front(vec[s-1][0]),Vis[vec[s-1][0].fir][vec[s-1][0].sec]=1;
int pos=1,siz=vec[s-1].size()-1;
while(!Q.empty())
{
auto u=Q.front();Q.pop_front();
while(pos<=siz&&Dist[u.fir][u.sec][to]==dis(vec[s-1][pos]))
{
if(!Vis[vec[s-1][pos].fir][vec[s-1][pos].sec]) Q.push_front(vec[s-1][pos]),Vis[vec[s-1][pos].fir][vec[s-1][pos].sec]=1;
++pos;
}
for(int k=1;k<=4;++k)
{
int nx=u.fir+dx[k],ny=u.sec+dy[k];
if(nx<1||nx>N||ny<1||ny>M||Vis[nx][ny]) continue;
Dist[nx][ny][to]=Dist[u.fir][u.sec][to]+1,Q.push_back({nx,ny});
Vis[nx][ny]=1;
}
}
for(int i=1;i<=N;++i) for(int j=1;j<=M;++j) if(a[i][j]!=s) Dist[i][j][to]=INF;
// for(int i=1;i<=N;++i,puts("")) for(int j=1;j<=M;++j) write(Dist[i][j][to],' ');
}
inline void print(int id)
{
for(int i=1;i<=N;++i,puts("")) for(int j=1;j<=M;++j) write(Dist[i][j][id],' ');
puts("");
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(N,M,P);
for(int i=1;i<=N;++i)
for(int j=1;j<=M;++j)
read(a[i][j]),vec[a[i][j]].push_back({i,j});
vec[0].push_back({1,1});
Dist[1][1][0]=0;
for(int i=1;i<=P;++i)
{
int a=vec[i-1].size(),b=vec[i].size();
if((ll)a*b<=4*N*M) calcCount(i);
else calcBfs(i);
}
write(Dist[vec[P][0].fir][vec[P][0].sec][P&1]);
return 0;
}

CF1392F

题目简介

题目名称:

题目来源:

评测链接:https://codeforces.com/problemset/problem/1392/F

形式化题意:给定一个长度为 的序列 ,每一时刻,遍历所有 ,如果存在 ,则使 ,注意每一时刻所有操作同时执行。求最后不存在变动时的序列 。保证 单调不递减。

数据范围:

显然,目标序列一定满足 ,且无论如何变化 永远不变。

考虑从 模拟会产生后效性,所以我们每次操作从 ,设当前位为

  • 如果 ,那么操作在此终止;
  • 否则 ,回到第一种情况。

考虑终止情况,要么直到 ,要么存在一个 满足 。如果是后者,那么 的对数就会减少 。而如果是前者,则不变。

这个 推进 次,会将之前所有相等数对全部清除,所以可以得到一个结论,就是 要么不存在,要么唯一。

所以,目标序列要么是一个满足 的序列,要么是两个满足那样的序列,且有

那我们首先构造第一种序列,然后考虑对其的某一个前缀 或者某一个后缀 即可。

(由等差数列公式推导),然后调整一下即可。

AC Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// ----- Eternally question-----
// Problem: F. Omkar and Landslide
// Contest: Codeforces - Codeforces Global Round 10
// URL: https://codeforces.com/problemset/problem/1392/F
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// Written by: Eternity
// Time: 2023-08-15 09:26:48
// ----- Endless solution-------

#include<bits/stdc++.h>
#define re register
typedef long long ll;
template<class T>
inline void read(T &x)
{
x=0;
char ch=getchar(),t=0;
while(ch<'0'||ch>'9') t|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
if(t) x=-x;
}
template<class T,class ...T1>
inline void read(T &x,T1 &...x1){ read(x),read(x1...); }
template<class T>
inline void write(T x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
template<>
inline void write(bool x){ putchar(x?'1':'0'); }
template<>
inline void write(char c){ putchar(c); }
template<>
inline void write(char *s){ while(*s!='\0') putchar(*s++); }
template<>
inline void write(const char *s){ while(*s!='\0') putchar(*s++); }
template<class T,class ...T1>
inline void write(T x,T1 ...x1){ write(x),write(x1...); }
template<class T>
inline bool checkMax(T &x,T y){ return x<y?x=y,1:0; }
template<class T>
inline bool checkMin(T &x,T y){ return x>y?x=y,1:0; }
const int MAXN=1e6+10;
int N;
ll H[MAXN],ans[MAXN],Sum;
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(N);
for(int i=1;i<=N;++i) read(H[i]),Sum+=H[i];
ans[1]=Sum/N-(N-1)/2;
Sum-=(2*ans[1]+N-1)*N/2;
for(int i=2;i<=N;++i) ans[i]=ans[i-1]+1;
for(int i=1;i<=Sum;++i) ++ans[i];
for(int i=-1;i>=Sum;--i) --ans[N+i+1];
for(int i=1;i<=N;++i) write(ans[i],' ');
return 0;
}
/*

*/

CF1483E Vabank

题目简介

题目名称:

题目来源:

评测链接:https://codeforces.com/problemset/problem/1483/E

形式化题意:交互题,猜一个数。你有一个分数 初始为 ,你猜数为 ,目标为 ,如果 ,那么 ,否则 。你需要保证任意时刻 ,猜数次数不超过 次。多测。

数据范围:

首先考虑一直猜 ,一边给 叠分,一边确定 的范围在 。此时在 范围内,大概是 次,而因为最后一次会减,所以

然后考虑用 次叠 ,然后二分,如果减了就再叠,这样的话最劣是 ,会超。

然后有两种优化方法,其一,是考虑三分或者四分,这样减的概率会少得多,但这个比较玄学,然后可以计算之后将分治的计算归在 左右就行。

第二种方法是用 的方法来计算 的阈值,类似于高楼扔鸡蛋的思路,可以得到一个较为准确的 ,这样下来,也就是 的做法,询问次数可以做到 以内。

AC Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// ----- Eternally question-----
// Problem: E. Vabank
// Contest: Codeforces - Codeforces Round 709 (Div. 1, based on Technocup 2021 Final Round)
// URL: https://codeforces.com/problemset/problem/1483/E
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// Written by: Eternity
// Time: 2023-08-15 21:20:58
// ----- Endless solution-------

#include<bits/stdc++.h>
#define re register
typedef long long ll;
template<class T>
inline void read(T &x)
{
x=0;
char ch=getchar(),t=0;
while(ch<'0'||ch>'9') t|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
if(t) x=-x;
}
template<class T,class ...T1>
inline void read(T &x,T1 &...x1){ read(x),read(x1...); }
template<class T>
inline void write(T x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
template<>
inline void write(bool x){ putchar(x?'1':'0'); }
template<>
inline void write(char c){ putchar(c); }
template<>
inline void write(char *s){ while(*s!='\0') putchar(*s++); }
template<>
inline void write(const char *s){ while(*s!='\0') putchar(*s++); }
template<class T,class ...T1>
inline void write(T x,T1 ...x1){ write(x),write(x1...); }
template<class T>
inline bool checkMax(T &x,T y){ return x<y?x=y,1:0; }
template<class T>
inline bool checkMin(T &x,T y){ return x>y?x=y,1:0; }
const ll LIM=1e14;
int Test;
char opt[20];
ll P,rp,lp,lim;
inline bool query(ll x)
{
std::cout<<"? "<<x<<std::endl;
std::cin>>opt;
return opt[0]=='L'?1:0;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(Test);
while(Test--)
{
P=lim=1;
while(true)
{
bool q=query(P);
if(q)
{
P<<=1,lim<<=1;
if(P>=LIM){ break; }
}
else{ P=0;break; }
}
if(lim==1){ std::cout<<"! 0"<<std::endl;continue; }
rp=std::min(LIM,lim-1),lp=(lim>>=1);
query(lp),query(lp);
P=lp<<1;
while(lp<rp)
{
ll lam=(P-rp+lp)/lp,q;
double r=(lam>=std::log2(rp-lp+1)?0.5:0.3+0.2*lam/std::log2(rp-lp+1));
if(rp-lp<=10) q=(rp+lp+1)>>1;
else q=lp*(1-r)+rp*r;
while(P<q) query(lp),P+=lp;
bool x=query(q);
if(x) P+=(lp=q);
else P-=q,rp=q-1;
}
std::cout<<"! "<<lp<<std::endl;
}
return 0;
}
/*

*/