8.25、8.26 训练总结
前言
昨天忘写了,今天补上。
A. 总评(surplus)
题面:
给定 \(n\) 个数 \(b_i\) 和 \(c_i\),请问在 \(\sum_{i=1}^n\lceil\frac{a_i}{b_i}\rceil-\sum_{i=1}^n\frac{a_i}{b_i}(0\le a_i\le c_i)\) 最大的情况下,数列 \(a_i\) 有多少种不同的情况。
显然当 \(a_i=b_i\cdot k_i+1\) 时这个差最大,所以问题变为我们要求数列 \(k_i\) 有多少种不同的取值方式。
根据 \(a_i\le c_i\) 我们可以得到 \(b_i\cdot k_i+1\le c_i\),转而变为 \(k_i\le\frac{c_i-1}{b_i}\),所以 \(k_i\) 的可能取值有 \(\lfloor\frac{c_i-1}{b_i}\rfloor+1\) 种。
特判一下当 \(c_i=0\) 时显然只有一种取值。
代码:
const int N=1e5+6,mod=1e9+7;
int n,b[N],c[N];
signed main()
{
int T=read();
while(T--)
{
n=read();
int ans=1;
for(int i=1;i<=n;i++)
{
b[i]=read(),c[i]=read();
if(b[i]==1)
{
ans=ans*(c[i]+1)%mod;
}
else if(c[i]==0)
{
continue;
}
else
{
ans=ans*((c[i]-1)/b[i]+1)%mod;
}
}
write(ans);
putchar('\n');
}
return 0;
}
B. 命运之树(parity)
题面:
给定一棵树,求所有可能DFS顺序中,偶数下标位置上节点能量和的最大值。
一眼 DP。
因为要分奇偶,所以可以想到设 \(f_{x,0/1}\) 表示在以 \(x\) 为根的子树中,\(x\) 在 dfs 序上处于奇数位置(或偶数位置)时能量的最大和。
初始化是显然的,我们考虑转移。
我们很容易想到一个点在 dfs 序上是在奇数位置还是偶数位置是和他前面的子树大小的和的奇偶性相关的,因此我们分奇偶讨论:
- 若 \(x\) 的所有子树大小都是偶数,那么显然每棵子树的根在 dfs 序上位置的奇偶性跟 \(x\) 的奇偶性是正好相反的,所以我们可以得到 \(f_{x,0}=\sum_{v\isin\operatorname{son}(x)}f_{v,1},f_{x,1}=\sum_{v\isin\operatorname{son}(x)}f_{v,0}\)。
- 若 \(x\) 的子树中有奇数大小的子树,我们记这个数量为 \(cnt\),那么你会发现我们把偶数大小的子树插在这些子树之间是不会影响奇偶性的,而经过一个奇数大小的子树奇偶性就会改变,所以对于每棵偶数大小的子树显然是在 \(f_{v,0},f_{v,1}\) 中取最大值。
对于奇数大小的子树来说:我们可以观察到其中必然有 \(\lceil\frac{cnt}{2}\rceil\) 棵子树奇偶性与 \(x\) 相反,\(\lfloor\frac{cnt}{2}\rfloor\) 棵子树奇偶性与 \(x\) 相同,因此我们只需要假设所有子树都是奇数(或偶数),然后用一个经典贪心就可以了。
代码:
const int N=2e5+6;
int n,siz[N],a[N],f[N][2];
vector<int>v[N];
void dfs(int x,int fa)
{
bool fl=false;
siz[x]=1;
f[x][0]=a[x],f[x][1]=0;
for(auto i:v[x])
{
if(i==fa)
{
continue;
}
dfs(i,x);
siz[x]+=siz[i];
if(siz[i]&1)
{
fl=true;
}
}
if(!fl)
{
for(auto i:v[x])
{
if(i==fa)
{
continue;
}
f[x][1]+=f[i][0],f[x][0]+=f[i][1];
}
}
else
{
int sum=0,cnt=0,sum0=0,sum1=0;
vector<int>v0,v1;
for(auto i:v[x])
{
if(i==fa)
{
continue;
}
if(!(siz[i]&1))
{
sum+=max(f[i][0],f[i][1]);
}
else
{
cnt++;
sum0+=f[i][1];
sum1+=f[i][0];
v0.push_back(f[i][0]-f[i][1]);
v1.push_back(f[i][1]-f[i][0]);
}
}
sort(v0.begin(),v0.end(),greater<int>());
sort(v1.begin(),v1.end(),greater<int>());
for(int i=0;i<cnt/2;i++)
{
sum0+=v0[i];
sum1+=v1[i];
}
f[x][0]+=sum0+sum,f[x][1]+=sum1+sum;
}
}
signed main()
{
int T=read();
while(T--)
{
n=read();
for(int i=1,x,y;i<n;i++)
{
x=read(),y=read();
v[x].push_back(y);
v[y].push_back(x);
}
read(a+1,a+n+1);
dfs(1,0);
write(f[1][1]);
putchar('\n');
for(int i=1;i<=n;i++)
{
v[i].clear();
}
}
return 0;
}
C. \(2\times100\)(relay)
题面:
初始给定 \(n\) 个数 \(a_i\) 和一个数 \(M\),第 \(i\) 个数的编号是 \(i\),你需要支持 \(q\) 次操作:
1 id x:插入一个编号为 \(id\) 的数 \(x\)(保证 \(id\) 不重复)。2 id:把编号为 \(id\) 的数删除。3:询问当前集合中,最多能匹配多少组,使得每一组的数的和 \(\le M\)。
说实话,考场上能写出来这道题我也是没想到。
首先前两个操作显然是好维护的,我们直接考虑第三个操作。
要求两个数之和小于等于 \(M\),我们还是来尝试给他们分类:
- 如果两个数都 \(\le\lfloor\frac{M}{2}\rfloor\),显然可以配对。
- 如果两个数都 \(\gt\lfloor\frac{M}{2}\rfloor\),显然无法配对。
问题就出在了一个 \(\le\lfloor\frac{M}{2}\rfloor\),一个 \(\gt\lfloor\frac{M}{2}\rfloor\) 上。
我们把这两种数分成两个数列 \(x\) 和 \(y\),那么如果一对这样的数要配对,需要满足的条件就是 \(x_i+y_j\le M\),移项一下就就是 \(y_j\le M-x_i\),所以我们可以先把所有的 \(x_i\) 变成 \(M-x_i\)。
然后我们会发现满足 \(y_j\le x'_i\)(此处的 \(x'_i\) 就是 \(M-x_i\))的 \(y_j\) 可能会有很多,因此我们考虑将 \(x'_i\) 看作一个点,\(y_j\) 看作一个点并分别列在左右两侧,如果 \(x'_i\) 和 \(y_j\) 满足条件就连一条边。
然后你会发现这个问题被我们转化成了二分图最大匹配问题!
二分图最大匹配问题用什么啊?对了,Hall 定理:
对于一个二分图,其中较小点集存在完美匹配的充要条件是这个点集的任意子集的邻域大小不小于这个子集大小。
搞错了,是推论:
对于一个二分图,其最大匹配为 \(|V|-\max_{S\subset V}\{|\operatorname{nx}(S)|-|S|\}\),特别的,因为 \(S\) 可以为空,所以这个式子大小必然小于等于 \(|V|\)。
显然由于 \(y_j\le x'_i\),那么点 \(x'_i\) 连接的点是一段区间,所以所有 \(x'_k\le x'_i\),\(\operatorname{nx}(x'_k)\subset\operatorname{nx}(x'_i)\),那么要求最大值,我们可以直接把这个推论的式子换一下:\([x'_j\le x'_i]-[y_j\le x'_i]\)。
然后就好做了:我们可以建一棵权值线段树,然后动态维护这个最大值,最后直接计算就行了。
代码:
const int N=4e5+6;
struct Query{
int op,id,x;
}Q[N];
bool b[N<<1];
multiset<int>ms[N<<1];
struct seg_tree{
struct Node{
int cnt1,cnt2,mx,lt;
}node[N<<3];
void build(int l,int r,int pos)
{
node[pos].cnt1=node[pos].cnt2=node[pos].lt=0,node[pos].mx=-1e18;
if(l==r)
{
return;
}
int mid=l+r>>1;
build(l,mid,pos<<1);
build(mid+1,r,pos<<1|1);
}
void pushup(int pos,int ls,int rs)
{
node[pos].mx=max(node[ls].mx,node[rs].mx);
node[pos].cnt1=node[ls].cnt1+node[rs].cnt1;
node[pos].cnt2=node[ls].cnt2+node[rs].cnt2;
}
void pushdown(int pos,int ls,int rs)
{
if(node[pos].lt)
{
node[ls].mx+=node[pos].lt,node[rs].mx+=node[pos].lt;
node[ls].lt+=node[pos].lt,node[rs].lt+=node[pos].lt;
node[pos].lt=0;
}
}
void updcnt(int pos,int l,int r,int x,int c1,int c2)
{
if(l==r)
{
node[pos].cnt1+=c1,node[pos].cnt2+=c2;
if(!node[pos].cnt1)
{
node[pos].mx=-1e18;
}
return;
}
pushdown(pos,pos<<1,pos<<1|1);
int mid=l+r>>1;
if(x<=mid)
{
updcnt(pos<<1,l,mid,x,c1,c2);
}
else
{
updcnt(pos<<1|1,mid+1,r,x,c1,c2);
}
pushup(pos,pos<<1,pos<<1|1);
}
void updmx(int pos,int l,int r,int x,int y)
{
if(l==r)
{
node[pos].mx=max(node[pos].mx,y);
return;
}
pushdown(pos,pos<<1,pos<<1|1);
int mid=l+r>>1;
if(x<=mid)
{
updmx(pos<<1,l,mid,x,y);
}
else
{
updmx(pos<<1|1,mid+1,r,x,y);
}
pushup(pos,pos<<1,pos<<1|1);
}
void update(int pos,int l,int r,int ql,int qr,int x)
{
if(ql>qr)
{
return;
}
if(!node[pos].cnt1)
{
return;
}
if(l>=ql&&r<=qr)
{
node[pos].mx+=x;
node[pos].lt+=x;
return;
}
pushdown(pos,pos<<1,pos<<1|1);
int mid=l+r>>1;
if(ql<=mid)
{
update(pos<<1,l,mid,ql,qr,x);
}
if(qr>mid)
{
update(pos<<1|1,mid+1,r,ql,qr,x);
}
pushup(pos,pos<<1,pos<<1|1);
}
int askc1(int pos,int l,int r,int ql,int qr)
{
if(l>=ql&&r<=qr)
{
return node[pos].cnt1;
}
pushdown(pos,pos<<1,pos<<1|1);
int mid=l+r>>1,ans=0;
if(ql<=mid)
{
ans+=askc1(pos<<1,l,mid,ql,qr);
}
if(qr>mid)
{
ans+=askc1(pos<<1|1,mid+1,r,ql,qr);
}
return ans;
}
int askc2(int pos,int l,int r,int ql,int qr)
{
if(l>=ql&&r<=qr)
{
return node[pos].cnt2;
}
pushdown(pos,pos<<1,pos<<1|1);
int mid=l+r>>1,ans=0;
if(ql<=mid)
{
ans+=askc2(pos<<1,l,mid,ql,qr);
}
if(qr>mid)
{
ans+=askc2(pos<<1|1,mid+1,r,ql,qr);
}
return ans;
}
}tr;
int n,m,a[N<<1];
vector<int>v;
signed main()
{
n=read(),m=read();
read(a+1,a+n+1);
int cnt=0;
for(int i=1;i<=n;i++)
{
if(a[i]<=m/2)
{
a[i]=m-a[i];
b[i]=1;
cnt++;
}
v.push_back(a[i]);
}
int q=read();
for(int i=1;i<=q;i++)
{
Q[i].op=read();
if(Q[i].op==1)
{
Q[i].id=read(),Q[i].x=read();
if(Q[i].x<=m/2)
{
Q[i].x=m-Q[i].x;
b[Q[i].id]=1;
}
v.push_back(Q[i].x);
}
else if(Q[i].op==2)
{
Q[i].id=read();
}
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
int len=v.size();
tr.build(1,len,1);
for(int i=1;i<=n;i++)
{
int it=lower_bound(v.begin(),v.end(),a[i])-v.begin()+1;
if(b[i])
{
tr.updcnt(1,1,len,it,1,0);
}
else
{
tr.updcnt(1,1,len,it,0,1);
}
}
for(int i=1;i<=n;i++)
{
int it=lower_bound(v.begin(),v.end(),a[i])-v.begin()+1;
if(b[i])
{
tr.updmx(1,1,len,it,tr.askc1(1,1,len,1,it)-tr.askc2(1,1,len,1,it));
}
}
for(int i=1;i<=q;i++)
{
int op=Q[i].op,id=0,x=0;
if(op==1)
{
id=Q[i].id,x=Q[i].x;
int it=lower_bound(v.begin(),v.end(),x)-v.begin()+1;
if(b[id])
{
cnt++;
tr.updcnt(1,1,len,it,1,0);
tr.updmx(1,1,len,it,tr.askc1(1,1,len,1,it)-tr.askc2(1,1,len,1,it));
tr.update(1,1,len,it+1,len,1);
}
else
{
tr.updcnt(1,1,len,it,0,1);
tr.update(1,1,len,it,len,-1);
}
a[id]=x;
}
else if(op==2)
{
id=Q[i].id;
x=a[id];
a[id]=0;
int it=lower_bound(v.begin(),v.end(),x)-v.begin()+1;
if(b[id])
{
cnt--;
tr.updcnt(1,1,len,it,-1,0);
tr.update(1,1,len,it,len,-1);
}
else
{
tr.updcnt(1,1,len,it,0,-1);
tr.update(1,1,len,it,len,1);
}
}
else
{
int mx=max(tr.node[1].mx,0ll);
write(cnt-mx+mx/2);
putchar('\n');
}
}
return 0;
}
D. Pocky游戏(game)
题面:
Mdk 和 Hmr 正在吃 Pocky,她们感到有些无聊,于是决定玩一个小游戏。
现在有一根长度为 \(n\) 的 Pocky,其中从左往右数第 \(i\) 单位长度 Pocky 的美味值为 \(a_i\)。现在 Mdk 和 Hmr 决定吃掉这根 Pocky,Mdk 从左往右吃,Hmr 从右往左吃。
- Mdk 先开始吃,她会从左侧吃 \(1\) 或 \(2\) 单位长度的 Pocky。
- 接下来,如果上一个人吃了 \(k\) 单位的 Pocky,则下一个人可以吃 \(k\) 或 \(k+1\)(如果剩余长度 \(\ge k+1\))单位的 Pocky。
- 如果剩下的 Pocky 长度小于 \(k\),则这个游戏将会停止。
双方都想要最大化自己吃掉的 Pocky 的美味值减掉对方吃掉的 Pocky 的美味值,问在双方都使用最优策略的情况下,Mdk 吃掉的 Pocky 的美味值减去 Hmr 吃掉的 Pocky 的美味值将会是多少?
首先你很容易想到一个 dfs。
然后你又很容易想到要记忆化它。
然后你写了一个 unordered_map,并计数了一下,发现在记忆化的情况下只需要跑接近 \(10^7\) 次就可以算出答案。但是你的代码跑了 3s。
显然常数太大了。
好的考虑如何记忆化。
首先把 dfs 摆出来:int dfs(int l,int r,int k,int op)。
我们惊人的发现:\(\sum_{i=1}^ki\le n\),于是我们得到了 \(k\le\sqrt{2n}\)。
我们又惊人的发现:\((n-r+1)-l\le k\le\sqrt{2n}\)。
于是就可以记忆化了。
代码:
const int N=4e3+6;
int n,cnt,a[N],s[N],f[N][106][106][2];
int dfs(int l,int r,int k,int op)
{
int len=r-l-1;
if(len<k)
{
if(op)
{
return s[n]-s[r-1]-s[l];
}
else
{
return s[l]-(s[n]-s[r-1]);
}
}
if(f[l][(n-r+1)-l][k][op])
{
return f[l][(n-r+1)-l][k][op];
}
int ans=-1e18;
if(op)
{
ans=max(ans,-dfs(l,r-k,k,0));
if(len>=k+1)
{
ans=max(ans,-dfs(l,r-k-1,k+1,0));
}
}
else
{
ans=max(ans,-dfs(l+k,r,k,1));
if(len>=k+1)
{
ans=max(ans,-dfs(l+k+1,r,k+1,1));
}
}
return f[l][r-l][k][op]=ans;
}
signed main()
{
n=read();
read(a+1,a+n+1);
for(int i=1;i<=n;i++)
{
s[i]=s[i-1]+a[i];
}
write(dfs(0,n+1,1,0));
return 0;
}
E. 宝石装置(gem)
题面:
Hmr 发现了一个宝石装置,并对其进行了研究:
这个装置由 \(n\) 颗宝石组成,依次编号为 \(1,2,\dots,n\)。这些宝石之间由总计 \(m\) 条路径连接。其中第 \(i\) 条路径可以由 \((u_i,v_i,a_i,b_i,x_i)\) 五个参数组成,代表着,在第 \(u_i\) 个宝石被激活之后,这条路径将会开始运作,在 \(a_ix_i+b_i\) 单位时间后,\(v_i\) 也将会被激活。其中 \(u_i,v_i,a_i,b_i\) 是固定的,\(x_i\) 可以由 Hmr 设置成任意非负实数。
在使用这个宝石装置的过程中,Hmr 可以在 \(0\) 时刻主动使用魔力激活任意一个节点 \(S\),然后宝石装置将会通过路径激活其他的宝石。如果一个宝石在不超过 \(V_i\) 时刻激活了,那么称这个宝石是有效的。
如果有效的宝石数量不小于 \(K\),那么这个装置将会开始工作,并释放出 \(\min_{i=1}^mx_i\)的能量。
Hmr 想要知道,这个装置最多可以释放多少能量。
求最小的最大值,显然要二分答案。
考虑 check 怎么写,显然每条边的边权是可以定的,那么我们可以求出每个点到起点的最短时间,这个用 Dijkstra 就行,可以得到时间复杂度大致为 \(O(n\log V(m\log n+n))\),显然过不了。
我们可以考虑尽量减少二分次数,因此我们可以先枚举起点,然后记录当前最优答案为 \(ans\),然后把当前起点和当前最优解放进去判断,如果不成立就没有二分的必要了,时间复杂度暂时未知。
但是我们会考虑到恶毒的出题人可能会专门卡这种做法,于是我们可以用魔法打败魔法:随机化!
在期望情况下,对一个序列随机打乱后的 LIS 长度大约是 \(2\sqrt{n}\),于是我们可以得到期望时间复杂度为 \(O(\sqrt{n}\log V(m\log n+n))\),7s 时限能过。
代码:
const int N=2e3+6,M=1e4+6;
const double eps=1e-6;
random_device rng;
struct edge{
int st,ed,a,b;
}e[M];
int n,m,k,a[N],p[N];
double dis[N];
bool vis[N];
vector<pair<int,double>>v[N];
bool check(int st,double x)
{
for(int i=1;i<=n;i++)
{
v[i].clear();
}
for(int i=1;i<=m;i++)
{
v[e[i].st].push_back(make_pair(e[i].ed,x*(double)(e[i].a)+(double)(e[i].b)));
}
priority_queue<pair<double,int>,vector<pair<double,int>>,greater<pair<double,int>>>pq;
for(int i=1;i<=n;i++)
{
dis[i]=1e18;
vis[i]=0;
}
int cnt=0;
dis[st]=0;
pq.push(make_pair(dis[st],st));
while(!pq.empty())
{
pair<double,int>p=pq.top();
pq.pop();
int u=p.second;
if(vis[u])
{
continue;
}
vis[u]=1;
if(dis[u]<=(double)(a[u]))
{
cnt++;
}
for(auto i:v[u])
{
if(dis[i.first]>dis[u]+i.second)
{
dis[i.first]=dis[u]+i.second;
pq.push(make_pair(dis[i.first],i.first));
}
}
}
return cnt>=k;
}
signed main()
{
n=read(),m=read(),k=read();
int id=read();
read(a+1,a+n+1);
for(int i=1;i<=n;i++)
{
p[i]=i;
}
for(int i=1;i<=m;i++)
{
e[i].st=read(),e[i].ed=read(),e[i].a=read(),e[i].b=read();
}
if(k==1)
{
write("999999999.999999");
putchar('\n');
return 0;
}
bool fl=false;
for(int i=1;i<=n;i++)
{
fl|=check(i,0);
}
if(!fl)
{
write(-1);
putchar('\n');
return 0;
}
shuffle(p+1,p+n+1,rng);
double ans=0;
for(int i=1;i<=n;i++)
{
if(!check(p[i],ans))
{
continue;
}
double l=ans,r=1e10;
while(r-l>=eps)
{
double mid=(l+r)/2;
if(check(p[i],mid))
{
ans=mid;
l=mid;
}
else
{
r=mid;
}
}
}
printf("%0.6lf\n",ans);
return 0;
}
F. 清洁机器人
题面。
显然的树形 DP。
我们会考虑到一条链又三种不同的可能:不跟父亲节点连接形成一条链、跟两个儿子节点连接形成一条链、跟父亲节点连接形成一条链。
因此我们设 \(f_{x,0/1/2}\) 表示这三种状态,于是我们可以写出转移:
然后就只需要优化一下就行了。
代码:
const int N=1e5+6,mod=1e9+7;
int n,pre[N],suf[N],p[N],s[N],sum[N],f[N][3];
vector<int>v[N];
int qpow(int x,int y)
{
int z=1;
while(y)
{
if(y&1)
{
z=z*x%mod;
}
x=x*x%mod;
y>>=1;
}
return z;
}
void dfs(int x,int fa)
{
vector<int>son;
son.push_back(0);
for(auto i:v[x])
{
if(i==fa)
{
continue;
}
dfs(i,x);
son.push_back(i);
}
int len=son.size()-1;
pre[0]=1,p[0]=1;
for(int i=1;i<=len;i++)
{
pre[i]=pre[i-1]*f[son[i]][1]%mod;
p[i]=p[i-1]*(f[son[i]][0]+f[son[i]][1])%mod;
}
suf[len+1]=1,s[len+1]=1;
sum[len+1]=0;
for(int i=len;i>=1;i--)
{
suf[i]=suf[i+1]*f[son[i]][1]%mod;
s[i]=s[i+1]*(f[son[i]][0]+f[son[i]][1])%mod;
}
for(int i=len;i>=1;i--)
{
sum[i]=(sum[i+1]*(f[son[i]][0]+f[son[i]][1])%mod+f[son[i]][2]*s[i+1]%mod)%mod;
}
for(int i=1;i<=len;i++)
{
f[x][0]=(f[x][0]+pre[i-1]*suf[i+1]%mod*f[son[i]][2]%mod)%mod;
f[x][1]=(f[x][1]+sum[i+1]*p[i-1]%mod*f[son[i]][2]%mod)%mod;
f[x][2]=(f[x][2]+p[i-1]*s[i+1]%mod*f[son[i]][2]%mod)%mod;
}
f[x][0]=(f[x][0]+pre[len])%mod;
f[x][2]=(f[x][2]+pre[len])%mod;
if(!len)
{
f[x][0]=f[x][2]=1,f[x][1]=0;
}
}
signed main()
{
n=read();
for(int i=1,x,y;i<n;i++)
{
x=read(),y=read();
v[x].push_back(y);
v[y].push_back(x);
}
dfs(1,0);
write((f[1][0]+f[1][1])%mod);
putchar('\n');
return 0;
}
G. Odd Rows
之前晚练题,目前题解第一篇还是我的。
挂个题解链接吧。
H. 联合猫国
题面。
我们先考虑以一个点 \(i\) 为结尾,要合成出 \(j\) 所需要的最近的起点,显然我们可以设 \(f_{i,j}\) 表示刚刚的状态,于是可以得到 \(f_{f_{i,j-1},j-1}\),这是显然的。
因此我们可以再设一个 \(g_i\) 表示考虑前 \(i\) 个数时的答案,那么我们会得到 \(g_i=\min_{j=0}\{g_{f_{i,j}}+1\}\)。
唯一的问题是 \(j\) 可能非常大,但是合并次数总共不超过 \(\log n\) 次,所以我们可以把第二维改成 \(j-a_i\)。
代码:
const int N=1e6+6;
int n,a[N],f[N];
vector<int>g[N];
signed main()
{
n=read();
for(int i=1;i<=n;i++)
{
a[i]=read();
int x=a[i];
g[i].push_back(i-1);
f[i]=f[i-1]+1;
int j=i-1;
while(j&&a[j]<=x&&x-a[j]<g[j].size())
{
j=g[j][x-a[j]];
x++;
f[i]=min(f[i],f[j]+1);
g[i].push_back(j);
}
}
write(f[n]);
putchar('\n');
return 0;
}
微微晨光点亮这喧嚣世界。
微微温暖融化昨夜的冰雪。
就像是每一秒都称为岁月。
微微从不停歇。
微微就是秋天里每片落叶。
微微就是彩虹里每滴雨点。
微微它很渺小却从不疲倦。
微微就是我们。

浙公网安备 33010602011771号