A. 很会dp
出题人: zhber
AC/TOT: 1/50
题解: 因为
V
V
V 实在太大了,所以做背包
d
p
dp
dp 肯定不行,这一点在题目中已经很明显的提示了。那么另一种做法只能是暴力枚举每一个物品取还是不取。每件物品可以选取或不取,这样方案数会有
2
n
2^n
2n,是不能接受的。注意到如果
n
n
n 变成
n
2
\frac{n}{2}
2n,暴力枚举似乎就可以接受了。再考虑到如果
n
n
n 件物品分成两部分,分别以
2
n
2
2^\frac{n}{2}
22n 枚举这两部分的所有可能的体积之和,合并的时候不需要在两区间枚举各取什么再加起来,因为这样还是
2
n
2^n
2n,而对于一个区间枚举取的是什么体积(假如取了体积是
s
u
m
sum
sum ),另一个区间排序之后直接二分小等于
V
−
s
u
m
V-sum
V−sum 的最大值即可。显然只有这个值对于
s
u
m
sum
sum 才是有用的,其他值要么加起来超过
V
V
V,要么加起来不如它优。因此对
n
n
n 个物品折半之后分别暴力处理所有可能体积,然后枚举前半部分,在后半部分二分即可。这个算法有个专门的名字,叫meet-in-middle
有兴趣的同学可以去学习一下“折半搜索”!
参考代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,len;
ll a[40],V,ans;
ll s[200010];
inline ll bsearch(int l,int r,ll x)
{
ll t=0;
while (l<=r)
{
int mid=(l+r)>>1;
if (s[mid]<=x)t=s[mid],l=mid+1;
else r=mid-1;
}
return t;
}
int main()
{
scanf("%d%lld",&n,&V);
for (int i=1;i<=n;i++)scanf("%lld",a+i);
for (int i=0;i<(1<<(n/2));i++)
{
ll sum=0;
for (int j=1;j<=n/2;j++)
if (i & (1<<(j-1)) )sum+=a[j];
if(sum<=V)s[++len]=sum;
}
s[++len]=0;
sort(s+1,s+len+1);
ans=s[len];
for (int i=0;i<(1<<(n-n/2));i++)
{
ll sum=0;
for (int j=n/2+1;j<=n;j++)
if (i & (1<<(j-n/2-1)) )sum+=a[j];
if(sum<=V)ans=max(ans,sum+bsearch(1,len,V-sum));
}
printf("%lld\n",ans);
}
B. 秀外慧中
出题人: zhber
AC/TOT: 0/0
题解: 注意到只要各位有一个零,那么乘积就是零了。因此 t t t是零与非零的情况分类讨论。
- 对于乘积为零的情况,考虑到用总方案数减去不合法方案数,即得到合法的方案数。总方案数是 1 0 k 10^k 10k,不合法方案就是 k k k 位都不是零,共 9 k 9^k 9k 个。所以答案就是 1 0 k − 9 k 10^k-9^k 10k−9k。注意答案取模必须是非负的。
- 对于乘积不为零的情况,注意到填的数字只能是 1...9 1...9 1...9,那么 t t t分解质因数其实只能有 2 、 3 、 5 、 7 2、3、5、7 2、3、5、7 四个质因数,否则一定无解。如果 t = 2 a 3 b 5 c 7 d t=2^a3^b5^c7^d t=2a3b5c7d,只要取 k k k 个数,乘起来得到 a a a 个 2 2 2、 b b b 个 3 3 3、 c c c 个 5 5 5、 d d d 个 7 7 7 即可。
考虑动态规划: f [ i ] [ j ] [ k ] [ l ] [ m ] f[i][j][k][l][m] f[i][j][k][l][m]表示前 i i i个位置、已经有 j j j 个 2 2 2、 k k k 个 3 3 3、 l l l 个 5 5 5、 m m m 个 7 7 7 的方案数,考虑取 1...9 1...9 1...9 每个数能贡献几个什么数字即可,比如 6 6 6 贡献了 1 1 1 个 2 2 2、 1 1 1 个 3 3 3,因此 f [ i ] [ j ] [ k ] [ l ] [ m ] + = f [ i − 1 ] [ j − 1 ] [ k − 1 ] [ l ] [ m ] f[i][j][k][l][m]+=f[i-1][j-1][k-1][l][m] f[i][j][k][l][m]+=f[i−1][j−1][k−1][l][m],其他 8 8 8 个式子同理。最后答案即为 f [ k ] [ a ] [ b ] [ c ] [ d ] f[k][a][b][c][d] f[k][a][b][c][d]。而 2 27 ≈ 3 17 ≈ 5 12 ≈ 7 10 ≈ 1 0 8 2^{27}\approx3^{17}\approx5^{12}\approx7^{10}\approx10^8 227≈317≈512≈710≈108,所以 a 、 b 、 c 、 d a、b、c、d a、b、c、d不会很大。
参考代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
int n,t;
int p[20],q[20],len;
ll f[50][30][20][15][15];
int main()
{
scanf("%d%d",&n,&t);
if (t==0)
{
int s1=1,s2=1;
for (int i=1;i<=n;i++)s1=(s1*10)%mod,s2=(s2*9)%mod;
printf("%d\n",(s1-s2+mod)%mod);
return 0;
}
for (int i=2;i*i<=t;i++)
if (t%i==0)
{
p[++len]=i;q[len]=1;t/=i;
while (t%i==0)t/=i,q[len]++;
}
if (len==0||t!=1)
{
p[++len]=t;
q[len]=1;
}
for (int i=1;i<=len;i++)if (p[i]!=1&&p[i]!=2&&p[i]!=3&&p[i]!=5&&p[i]!=7){puts("0");return 0;}
int a=0,b=0,c=0,d=0;
for(int i=1;i<=len;i++)
{
if (p[i]==2)a=q[i];
if (p[i]==3)b=q[i];
if (p[i]==5)c=q[i];
if (p[i]==7)d=q[i];
}
f[0][0][0][0][0]=1;
for (int i=1;i<=n;i++)
for (int j=0;j<=a;j++)
for (int k=0;k<=b;k++)
for(int l=0;l<=c;l++)
for (int m=0;m<=d;m++)
{
f[i][j][k][l][m]+=f[i-1][j][k][l][m];//1
if (j>=1)f[i][j][k][l][m]+=f[i-1][j-1][k][l][m];//2
if (k>=1)f[i][j][k][l][m]+=f[i-1][j][k-1][l][m];//3
if (j>=2)f[i][j][k][l][m]+=f[i-1][j-2][k][l][m];//4
if (l>=1)f[i][j][k][l][m]+=f[i-1][j][k][l-1][m];//5
if (j>=1&&k>=1)f[i][j][k][l][m]+=f[i-1][j-1][k-1][l][m];//6
if (m>=1)f[i][j][k][l][m]+=f[i-1][j][k][l][m-1];//7
if (j>=3)f[i][j][k][l][m]+=f[i-1][j-3][k][l][m];//8
if (k>=2)f[i][j][k][l][m]+=f[i-1][j][k-2][l][m];//9
f[i][j][k][l][m]%=mod;
}
printf("%lld\n",f[n][a][b][c][d]);
}
C. 背着大家偷偷交题
出题人: zhber
AC/TOT: 0/0
题解: 把所有熟人的位置扔进队列,然后做 b f s bfs bfs 往外扩展,即可知道每个点到它最近熟人的距离 d i s t [ i ] [ j ] dist[i][j] dist[i][j]。显然如果存在一条最小距离是 k k k 的路径,也肯定存在一条最小距离是 k − 1 k-1 k−1 的路径,因此最大的最小距离会在 [ k , + ∞ ) [k,+\infty) [k,+∞)。而如果不存在最小距离是 k k k 的路径,那也一定不存在最小距离是 k + 1 k+1 k+1 的路径,因此最大的最小距离会在 [ 0 , k ) [0,k) [0,k)。因此可以二分最小距离,不管我们判断某个答案可不可行,一定可以将范围至少缩小一半。考虑判定最小距离 m i d mid mid 可不可行,此时只能走满足 d i s t [ x ] [ y ] ≥ m i d dist[x][y]\ge mid dist[x][y]≥mid 的点 ( x , y ) (x,y) (x,y),相当于在原有条件下多了一个合法性的判定。在此条件下, b f s bfs bfs 求 S S S 到 T T T 的最短距离即可。
参考代码:
#include<bits/stdc++.h>
#define pa pair<int,int>
#define mkp make_pair
using namespace std;
char mp[1010][1010];
int dist[1010][1010];
int dis2[1010][1010];
int n,m;
int sx,sy,ex,ey;
int mx[4]={1,0,-1,0},my[4]={0,1,0,-1};
inline void bfs1()
{
memset(dist,-1,sizeof dist);
queue<pa>q;while (!q.empty())q.pop();
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (mp[i][j]=='#')q.push(mkp(i,j)),dist[i][j]=0;
while (!q.empty())
{
int nx=q.front().first,ny=q.front().second;q.pop();
for (int k=0;k<4;k++)
{
int wx=nx+mx[k];
int wy=ny+my[k];
if (wx<1||wx>n||wy<1||wy>m||dist[wx][wy]!=-1)continue;
dist[wx][wy]=dist[nx][ny]+1;
q.push(mkp(wx,wy));
}
}
}
inline int bfs2(int x)
{
memset(dis2,-1,sizeof dis2);
queue<pa>q;while (!q.empty())q.pop();
q.push(mkp(sx,sy));dis2[sx][sy]=0;
while (!q.empty())
{
int nx=q.front().first,ny=q.front().second;q.pop();
for (int k=0;k<4;k++)
{
int wx=nx+mx[k];
int wy=ny+my[k];
if (wx<1||wx>n||wy<1||wy>m||dis2[wx][wy]!=-1||dist[wx][wy]<x)continue;
dis2[wx][wy]=dis2[nx][ny]+1;
q.push(mkp(wx,wy));
}
}
return dis2[ex][ey];
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)scanf("%s",mp[i]+1);
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
if (mp[i][j]=='S')sx=i,sy=j;
else if (mp[i][j]=='T')ex=i,ey=j;
}
bfs1();
int l=0,r=2*n-1,ans=0,ans2=0;
while (l<=r)
{
int mid=(l+r)>>1,cal=bfs2(mid);
if (cal!=-1)ans=mid,ans2=cal,l=mid+1;
else r=mid-1;
}
printf("%d %d\n",ans,ans2);
}
D. I Love Diamond Forever.jpg
出题人: konnyaku
AC/TOT: 8/52
题解:
diamond裸 dp,不会就把讲 dp 的学长拖出去枪毙五分钟 。 ——天爸爸
卖完萌还是讲一下吧,分析一下问题,钻石血量多少不会影响它受多少伤害,反而一旦月狗质量确定,则钻石受的伤害是确定的,明白了这一点,这符合动态规划的子问题性质,考虑 f ( i ) f(i) f(i) 表示一只血量为 i i i 的月狗能对钻石造成的最小伤害,它下一轮只会转移到 f ( i ) → f ( i − k ) + f ( k ) , 0 < k < i f(i)\to f(i-k)+f(k),\ 0< k< i f(i)→f(i−k)+f(k), 0<k<i 枚举 k k k 就好了,而且因为有对称性, k k k 只需要枚举到 [ i 2 ] [\frac{i}{2}] [2i] 就好了,由于 N N N 和 M M M 都很大我们可以考虑记忆化搜索。
参考代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 5;
int a[maxn];
ll f[maxn];
ll DP(int i) {
if (f[i] != -1) return f[i];
if (i == 1) return 0;
ll ans = 1e18;
for (int k = 1; k <= i / 2; k++) ans = min(ans, DP(k) + DP(i - k) + a[k] + a[i - k]);
return f[i] = ans;
}
int main() {
int m, n;
cin >> m >> n;
a[1] = 0;
for (int i = 2; i <= m; i++) cin >> a[i];
memset(f, -1, sizeof f);
ll res = DP(m);
if (res >= n) {
puts("NO");
cout << res - n + 1 << endl;
} else {
puts("YES");
cout << n - res << endl;
}
return 0;
}
E. 线段数不清了QAQ
出题人: cwhbbt
AC/TOT: 29/64
题解:
- 先考虑 0 0 0 为左端点, n n n 为右端点这片区域的所有小线段:它们两两匹配,所构成的 X X X 线总数为 n n n
- 再考虑以 1 1 1 为左端点, n − 1 n-1 n−1 为右端点这片区域的所有小线段:它们两两匹配,所构成的 X X X 线总数为 n − 2 n-2 n−2
- 以此类推…累加即可
观察以后不难推出答案为 n + 1 2 ⋅ [ n − ( n − 1 ) 2 ] \frac{n+1}{2}·[n-\frac{(n-1)}{2}] 2n+1⋅[n−2(n−1)] 。
参考代码:
#include<bits/stdc++.h>
using namespace std;
int main() {
int n;
while(cin>>n) cout<<(n+1)/2*(n-(n-1)/2)<<endl;
return 0;
}
F. 龙神和平行四边形
出题人: sadyi98
AC/TOT: 1/23
题解:
- 我们先不考虑要拆线段的情况,由于平行四边形对边相等,所以该题就是要我们找有多少对线段长度是相等的,再将数量除二下取整就可以了。
- 再考虑要拆线段的情况,简单分析一下就可以知道,如果我们拆的是某一对边中的一条,根本没有什么**用,那我们就只需要考虑剩下的所有单个的边的情况:
- 当我们有两条以上的孤立边的时候,显然我们至少可以将较大的线段截成较小的线段组成一对。
- 然后我们考虑更多的情况,如果我们能在孤立边中找到某一条线段是其他两条线段之和,那我们可以多凑出两对相等的线段。
- 当我们只有一条孤立边的时候,如果该孤立边是偶数长度,则其也可拆成一对相等的线段。
- 再回到之前的数量除二下取整就可以得出答案。
参考代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<vector>
#include<set>
#define mem(a,b) memset((a),(b),sizeof(a))
using namespace std;
const double pi=acos(-1.0);
int l[2005];
map<int,int> m;
vector<int> v1;
vector<int> v2;
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&l[i]);
}
sort(l,l+n);
l[n]=-1;
int temp=0;
for(int i=0;i<n;i++)
{
if(l[i+1]==l[i])
{
i++;
v2.push_back(l[i]);
temp++;
continue;
}
else
{
m[l[i]]=1;
v1.push_back(l[i]);
}
}
int flag=0;
for(int i=0;i<v1.size()&&flag==0;i++)
{
for(int j=i;j<v1.size()&&flag==0;j++)
{
if(m.count(v1[i]+v1[j]))
{
flag=2;
}
}
}
if(flag==0&&v1.size())
{
if(v1.size()>1||(v1[0]&1)==0)flag=1;
}
printf("%d\n",(temp+flag)/2);
}
G. 丑闻传播计划
出题人: sairyo
AC/TOT: 1/25
题解: 考虑两遍 b f s bfs bfs 处理这个关系图——
- 第一遍 b f s bfs bfs 从龙神开始找出所有可能告诉龙神消息的人,标记好,显然能告诉龙神消息的人都是不能传播的。
- 第二遍 b f s bfs bfs 从小明开始找出小明到妹子最少经过多少个人,遇见被第一遍标记过的人则直接剪枝。
参考代码:
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<set>
#include<stack>
#include<queue>
#include<cstring>
#include<map>
#include<vector>
#define maxn 1000010
using namespace std;
struct road{
int nxt,to;
}E[3*maxn];
int n,k,vis[maxn],tim[maxn];
int main(){
scanf("%d%d",&n,&k);
memset(E,-1,sizeof(E));
int now=n+1;
for(int i=0;i<k;i++){
int a,b;
scanf("%d%d",&a,&b);
E[now].nxt=E[a].nxt;
E[now].to=b;
E[a].nxt=now++;
E[now].nxt=E[b].nxt;
E[now].to=a;
E[b].nxt=now++;
}
queue<int>l;
l.push(n-1);
while(!l.empty()){
int u=l.front();l.pop();
for(int i=E[u].nxt;i!=-1;i=E[i].nxt){
if(vis[E[i].to]||E[i].to==n||E[i].to==1)continue;
vis[E[i].to]=1;
l.push(E[i].to);
}
}
l.push(1);
tim[1]=-1;
while(!l.empty()){
int u=l.front();l.pop();
for(int i=E[u].nxt;i!=-1;i=E[i].nxt){
if(vis[E[i].to])continue;
vis[E[i].to]=1;
tim[E[i].to]=tim[u]+1;
if(E[i].to==n){
printf("%d\n",tim[E[i].to]);
return 0;
}
l.push(E[i].to);
}
}
printf("-1\n");
}
H. 龙神的正义感
出题人: miamiao
AC/TOT: 9/65
题解: 使得一个图联通,可以考虑它的生成树,本题要求最大的 k k k 使得它联通,容易想到是一个图的最大生成树(这个喵神应该讲过?和最小生成树一样的!),因此我们直接对这个图求一遍最大生成树,这颗树最小的边就是答案!
参考代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000010;
typedef long long LL;
struct node
{
int u,v;
LL w;
}a[maxn];
int sz = 1,fa[maxn];
bool cmp(const node &aa,const node &bb){return aa.w>bb.w;}
int tot ;
int find(int x)
{
if(fa[x] == -1)return x;
int r = fa[x];
while(fa[r]!=-1)
{
fa[x] = fa[r];
x = r;
r = fa[x];
}
return fa[x];
}
void uni(int a,int b)
{
int ra = find(a),rb = find(b);if(ra == rb)return;
fa[rb] = ra;sz++;
}
int main(int argc, char const *argv[])
{
memset(fa,-1,sizeof fa);
int n,m;scanf("%d%d",&n,&m);
for(int i = 1;i<= m;i++)scanf("%d%d%lld",&a[i].u,&a[i].v,&a[i].w);
sort(a+1,a+1+m,cmp);
for(int i = 1;i<= m;i++)
{
uni(a[i].u,a[i].v);
if(sz == n){printf("%lld\n",a[i].w);return 0;}
}
printf("-1\n");
return 0;
}
I. 龙神与甜甜圈
出题人: FSMM
AC/TOT: 18/109
题解: 考虑贪心地选择价格低的那次购买,如果第一次价格较低的甜甜圈个数小于
k
k
k 个,就尽量购买第一次与第二次价格差值小的甜甜圈。因此按照第一次与第二次价格的差值排序,差值相同按照编号排序,然后依次选取。唯一的trick是注意要使用long long
。
参考代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 200005
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;
struct Ring
{
ll x, y, z;
int id;
}a[maxn];
bool cmp(Ring a, Ring b)
{
if (a.z == b.z) return a.id < b.id;
return a.z < b.z;
}
int main()
{
int n, k, ans[maxn];
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++) scanf("%I64d", &a[i].x);
for (int i = 1; i <= n; i++) {
scanf("%I64d", &a[i].y);
a[i].z = a[i].x - a[i].y;
a[i].id = i;
}
sort(a + 1, a + n + 1, cmp);
int num = 0, cot = 0;
ll sum = 0;
for (int i = 1; i <= n; i++) {
if (a[i].z <= 0 || num < k) {
sum += a[i].x;
num++;
ans[++cot] = a[i].id;
}
else sum += a[i].y;
}
sort(ans + 1, ans + cot + 1);
printf("%lld\n", sum);
printf("%d\n", cot);
for (int i = 1; i <= cot; i++)
i == cot ? printf("%d\n", ans[i]) : printf("%d ", ans[i]);
return 0;
}
J. 龙神与地下城
出题人: 阿尔西斯
AC/TOT: 0/61
题解: 不难看出是搜索题。每
k
k
k 秒会消失看似棘手,仔细分析每个点只有
k
k
k 种状态,不如假设第
i
i
i 秒真正有效的状态是
i
%
k
i\%k
i%k,我们可以考虑在
b
f
s
bfs
bfs 的vis[x][y]
状态数组的基础上增加一个维度表示vis[x][y][z]
其中
x
,
y
x,y
x,y 表示坐标
z
z
z 表示第
i
i
i 秒取模
k
k
k 的状态
i
%
k
i\%k
i%k ,接下来问题就转换成了简单的带障碍的2D搜索,大家都会做。
参考代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e2 + 5;
const int INF = 0x3f3f3f3f;
const int dx[] = {1, 0, -1, 0};
const int dy[] = {0, 1, 0, -1};
struct node{
int x, y, step;
node(int _x = 0, int _y = 0, int _step = 0){
x = _x;
y = _y;
step = _step;
}
};
queue<node> q;
char mp[maxn][maxn];
bool vis[maxn][maxn][52];
int main() {
int n, m, k, sx, sy, T, i, j;
scanf("%d", &T);
while(T--){
scanf("%d%d%d", &n, &m, &k);
getchar();
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
mp[i][j] = getchar();
if(mp[i][j]=='S')
sx = i, sy = j;
}
getchar();
}
int ans = INF;
memset(vis, 0, sizeof(vis));
while(!q.empty()) q.pop();
q.push(node(sx, sy, 0));
vis[sx][sy][0] = 1;
while(!q.empty()){
node tmp = q.front(); q.pop();
if(mp[tmp.x][tmp.y]=='E'){
ans = tmp.step;
break;
}
for(i=0;i<4;i++){
int x = tmp.x + dx[i], y = tmp.y + dy[i], stp = tmp.step+1;
if(x<1 || x>n || y<1 || y>m || mp[x][y]=='#' || vis[x][y][stp%k]) continue;
if(mp[x][y]=='*' && (stp%k)) continue;
q.push(node(x, y, stp));
vis[x][y][stp%k] = 1;
}
}
if(ans==INF) printf("-1\n");
else printf("%d\n", ans);
}
return 0;
}
K. 这真的是签到题
出题人: strawberry
AC/TOT: 25/91
题解: 显然矩形越大则能采摘的草莓越多,观察后不难发现矩形一定是向样例这样的,两边分别取 x = 0 , y = 0 x=0,\ y=0 x=0, y=0,另外一个端点在直线上,也很容易证明如果点在区域内,显然有一个更优的解能将点扩展到直线上,因此我们直接枚举直线上的整点,暴力统计矩形内的整点就好了。枚举直线上的整点可以考虑枚举每一个整数 x x x ,再对每一个 y y y 求和,求和可以暴力求,复杂度 O ( b m ) O(bm) O(bm) ,当然单行的公式也很好推,大家可以尝试推一下,这样复杂度是 O ( b ) O(b) O(b)。还有一个拓展可以想想看:你能 O ( 1 ) O(1) O(1) 得出结果吗?
- 唯一的trick就是需要使用
64
位整数,这个也在题目暗示明示了,很良心吧?
参考代码:
#include <bits/stdc++.h>
typedef long long ll;
int main()
{
int m, b;
scanf("%d%d", &m, &b);
int x0 = m * b, y0 = b;
ll ans = 0;
for (int i = 0; i <= y0; i++)
{
int y_i = i;
int x_i = m * (b - y_i);
ll tmp = (1LL + x_i) * (y_i + 1) * x_i / 2 + (1LL + y_i) * (x_i + 1) * y_i / 2;
ans = max(ans, tmp);
// printf("%lld : %d %d\n", tmp, x_i, y_i);
}
printf("%lld\n", ans);
return 0;
}
冬训简单总结
很感谢一直坚持下来的各位同学,不管你们是对ACM
充满着热情,还是抱着其他学习的目的,希望这个集训能有所收获,感受到算法带给大家的巨大魅力。出题人出的题不一定能完全反映出你的所有真实水平,但是我们希望每一次比赛你都有所成长和提高,善于分析总结自己的不足,这是在今后学习和生活的利器!也希望有兴趣的同学继续坚持下去,积极补题,查缺补漏,把冬训没做好的事情做得更加完美,下一轮积分赛等着大家,BITACM欢迎你的加入!