A. Multiples of Length
略
B. Stoned Game
略
C. Monster Invaders
问题可以转化为从level
1
1
1开始,每次花费
d
d
d的时间移动到一个相邻的level,然后对于level
i
i
i,我们有两种选择:第一种是花费
p
i
=
r
1
a
i
+
r
3
p_i=r_1a_i+r_3
pi=r1ai+r3时间,需要至少经过level
i
i
i一次;第二种是花费
q
i
=
min
(
r
1
(
a
i
+
2
)
,
r
1
+
r
2
)
q_i=\min(r_1(a_i+2),r_1+r_2)
qi=min(r1(ai+2),r1+r2)时间,需要至少经过level
i
i
i两次,问最小花费时间。
考虑
f
i
f_i
fi(
i
<
n
i<n
i<n)表示我们第一次到达level
i
i
i,不返回level
i
−
1
i-1
i−1以前,干掉level
i
i
i到level
n
n
n的所有怪的最小时间。那么显然有几种策略:第一种是只经过level
i
i
i一次,花费
f
i
+
1
+
d
+
p
i
f_{i+1}+d+p_i
fi+1+d+pi时间;否则后面还会返回level
i
i
i,那么我们考虑第一次返回level
i
i
i前的最高level
k
k
k,若
k
<
n
k<n
k<n显然我们下一次会直接从level
i
i
i走到level
k
+
1
k+1
k+1,再也不会返回level
k
k
k(
k
=
n
−
1
k=n-1
k=n−1可能要特殊讨论),这样花费
(
∑
j
=
i
k
min
(
p
j
,
q
j
)
)
+
(
3
(
k
−
i
)
+
1
)
d
+
f
k
+
1
(\sum_{j=i}^{k}\min(p_j,q_j))+(3(k-i)+1)d+f_{k+1}
(∑j=ikmin(pj,qj))+(3(k−i)+1)d+fk+1时间;否则
k
=
n
k=n
k=n且返回level
i
i
i,这样显然除level
n
n
n都至少走了两次,简单讨论一下是否在level
n
−
1
n-1
n−1和level
n
n
n之间折返即可。
倒着做上述DP,容易实现到
O
(
n
)
\mathcal O(n)
O(n)。
#include <bits/stdc++.h>
#define FR first
#define SE second
#define y1 yy
#define last last2
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef pair<int,int> pr;
typedef long long ll;
ll val1[1000005],val2[1000005];
ll f[1000005],sum[1000005];
int main() {
int n;
ll a,b,c,d;
scanf("%d%lld%lld%lld%lld",&n,&a,&b,&c,&d);
for(int i=1;i<=n;i++) {
int x;
scanf("%d",&x);
val1[i]=a*x+c;
val2[i]=min(a+b,a*(x+2));
}
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+min(val1[i],val2[i]);
f[n]=val1[n];
ll minn=inf;
for(int i=n-1;i>0;i--) {
f[i]=min(f[i+1]+val1[i]+d,min(d*min(2*(n-i)+2,3*(n-i))+sum[n]-sum[i-1],d*(2*(n-i))+sum[n-1]-sum[i-1]+val1[n]));
if (i+2<=n) minn=min(minn,f[i+2]+d*(3*(i+2)-2)+sum[i+1]);
if (minn<inf) f[i]=min(f[i],minn-d*(3*i)-sum[i-1]);
}
printf("%lld\n",f[1]);
return 0;
}
D. Rainbow Rectangles
首先将坐标离散化到
[
1
,
n
]
[1,n]
[1,n]之间。
枚举矩形的右边界
x
2
x_2
x2,对左边界
x
1
x_1
x1从小到大做扫描线。若
x
1
=
1
x_1=1
x1=1,我们容易用two pointer得到每个下边界
y
1
y_1
y1对应的最小上边界
y
2
y_2
y2(所有
≥
Y
2
\geq Y_2
≥Y2的显然都可行)。每次增大
x
1
x_1
x1,意味着我们需要删除若干个点,若我们删除了某个
(
x
i
,
y
i
,
c
i
)
(x_i,y_i,c_i)
(xi,yi,ci),那么我们考虑
c
=
c
i
c=c_i
c=ci且还未被删除的点中,若还有
y
=
y
i
y=y_i
y=yi的显然不会影响,否则考虑
y
<
y
i
y<y_i
y<yi的最大的
y
l
y_l
yl(不存在设为
0
0
0),
y
>
y
i
y>y_i
y>yi的最小的
y
r
y_r
yr(不存在设为
n
+
1
n+1
n+1),那么我们会将所有
y
∈
(
y
l
,
y
i
]
y\in(y_l,y_i]
y∈(yl,yi]的最小上边界与
y
r
y_r
yr取
max
\max
max。注意到任何时刻每个
y
y
y对应的最小上边界单调不降,那么可以用set维护连续段的trick,均摊
O
(
log
n
)
\mathcal O(\log n)
O(logn)更新。
总时间复杂度为
O
(
n
2
log
n
)
\mathcal O(n^2\log n)
O(n2logn)。
#include <bits/stdc++.h>
#define FR first
#define SE second
#define y1 yy
#define last last2
#define inf 0x3f3f3f3f3f3f3f3f
#define MOD 1000000007
#define last last2
using namespace std;
typedef pair<int,int> pr;
typedef long long ll;
int xv[2005],yv[2005];
set <int> st1;
multiset <int> st2[2005];
int val[2005];
struct Point {
int x,y,c;
Point() {}
Point(int a,int b,int c):x(a),y(b),c(c) {}
};
bool cmp1(Point x,Point y) {
return x.x<y.x;
}
bool cmp2(Point x,Point y) {
return x.y<y.y;
}
Point p[2005],q[2005];
int cnt[2005];
int main() {
int n,m,L;
scanf("%d%d%d",&n,&m,&L);
for(int i=1;i<=n;i++) {
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
x++;y++;
p[i]=Point(x,y,z);
xv[i]=x;
yv[i]=y;
}
sort(xv+1,xv+n+1);
sort(yv+1,yv+n+1);
int sz1=unique(xv+1,xv+n+1)-xv-1,sz2=unique(yv+1,yv+n+1)-yv-1;
xv[sz1+1]=yv[sz2+1]=L+1;
for(int i=1;i<=n;i++) {
p[i].x=lower_bound(xv+1,xv+sz1+1,p[i].x)-xv;
p[i].y=lower_bound(yv+1,yv+sz2+1,p[i].y)-yv;
q[i]=p[i];
}
sort(p+1,p+n+1,cmp1);
sort(q+1,q+n+1,cmp2);
ll ans=0;
for(int i=1;i<=sz1;i++) {
memset(cnt,0,sizeof(cnt));
ll s=0;
int r1=n,r2=n,csz=0;
for(int j=sz2;j>0;j--) {
while (r1&&q[r1].y>=j) {
if (q[r1].x<=i) {
if (!cnt[q[r1].c]) csz++;
cnt[q[r1].c]++;
}
r1--;
}
if (csz>=m) {
while (r2&&q[r2].y>=j) {
if (q[r2].x>i) r2--;
else if (cnt[q[r2].c]>1) {
cnt[q[r2].c]--;
r2--;
}
else break;
}
val[j]=q[r2].y;
}
else val[j]=sz2+1;
s=(s+(ll)(L+1-yv[val[j]])*(yv[j]-yv[j-1]))%MOD;
}
st1.clear();
for(int j=0;j<=m;j++) st2[j].clear();
for(int j=1;j<=sz2;j++)
if (j==sz2||val[j]!=val[j+1]) st1.insert(j);
for(int j=1;j<=n;j++)
if (p[j].x<=i) st2[p[j].c].insert(p[j].y);
r1=1;
for(int j=1;j<=i;j++) {
ans=(ans+s*(xv[j]-xv[j-1])%MOD*(xv[i+1]-xv[i]))%MOD;
while (r1<=n&&p[r1].x<=j) {
multiset<int>::iterator it=st2[p[r1].c].lower_bound(p[r1].y),it2=it;
it2++;
if (it2!=st2[p[r1].c].end()&&(*it2)==(*it)) {
st2[p[r1].c].erase(it);
r1++;
continue;
}
it2=it;
int l;
if (it2==st2[p[r1].c].begin()) l=1;
else {
it2--;
l=(*it2)+1;
}
int r;
it2=it;
it2++;
if (it2==st2[p[r1].c].end()) r=sz2+1;
else r=(*it2);
st2[p[r1].c].erase(it);
set<int>::iterator it3=st1.lower_bound(l),it4=it3;
if (val[*it3]>=r) {
r1++;
continue;
}
if (it4!=st1.begin()) {
it4--;
if ((*it4)<l-1) {
val[l-1]=val[*it3];
st1.insert(l-1);
}
}
else if (l>1) {
val[l-1]=val[*it3];
st1.insert(l-1);
}
int last=l-1;
for(;it3!=st1.end()&&val[*it3]<r;it3=st1.lower_bound(l)) {
int cur=(*it3);
s=(s-(ll)(yv[r]-yv[val[cur]])*(yv[cur]-yv[last])%MOD+MOD)%MOD;
st1.erase(it3);
last=cur;
}
val[last]=r;
st1.insert(last);
r1++;
}
}
}
printf("%lld\n",ans);
return 0;
}
E. Distance Matching
首先将无根树转化为以重心
c
c
c为根。
对于某个匹配
i
−
j
i-j
i−j,贡献为
d
e
p
i
+
d
e
p
j
−
2
⋅
d
e
p
l
c
a
(
i
,
j
)
dep_i+dep_j-2\cdot dep_{lca(i,j)}
depi+depj−2⋅deplca(i,j)。那么可以把最后答案转化为只跟每组匹配的lca深度和有关。容易用贪心思想得到最小的距离和
k
l
k_l
kl,最大的距离和
k
r
k_r
kr(事实上此时所有的lca
=
c
=c
=c)。显然
k
l
k_l
kl与
k
r
k_r
kr奇偶性相同,那么若
k
∉
[
k
l
,
k
r
]
k\notin[k_l,k_r]
k∈/[kl,kr]或奇偶性不相同显然无解,否则下面的构造可以给出一组解。
注意到我们对达到
k
l
k_l
kl的方案中,每将一个lca改为父亲的话可以把距离和增加
2
2
2。我们尝试不断将某个lca换成它父亲,直到距离和达到
k
k
k为止。我们可以发现若我们找到某个子树中深度最浅的lca,将它不断上移的话,矛盾只可能出现在将它移到
c
c
c那里。由于
c
c
c是重心,我们如果每次选择一个里面还有lca,且
s
i
z
e
−
2
⋅
size-2\cdot
size−2⋅剩余
l
c
a
lca
lca个数最小的子树,将它里面某个lca往上移动一定不会出现矛盾。这样先搞出每次移动的子树顺序,再移动深度最小的lca,就可以构造出一组合法的lca方案了。
现在得到lca后还要还原匹配,这是容易做到的。我们可以dfs一遍,每次匹配完某个点子树内的,再匹配lca是它的。这是一个经典问题,每次在两个剩余点数最大的子树内各取一个点匹配即可。
这里需要优先队列,时间复杂度为
O
(
n
log
n
)
\mathcal O(n\log n)
O(nlogn)。
#include <bits/stdc++.h>
#define FR first
#define SE second
#define last last2
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
vector<int> calc1(vector<int> vt) {
priority_queue<pr,vector<pr>,greater<pr>> q;
for(int i=0;i<vt.size();i++)
if (vt[i]>1) q.push(pr((vt[i]&1),i));
vector<int> que;
while (!q.empty()) {
pr t=q.top();q.pop();
que.push_back(t.SE);
if (t.FR+2<vt[t.SE]) q.push(pr(t.FR+2,t.SE));
}
return que;
}
vector<pr> calc2(int n,vector<int> vt) {
priority_queue<pr> q;
for(int i=0;i<vt.size();i++) q.push(pr(vt[i],i));
int last=0;
vector<pr> que;
for(int i=1;i<=n;i++) {
pr t1=q.top();q.pop();
pr t2=q.top();q.pop();
que.push_back(pr(t1.SE,t2.SE));
if (t1.FR>1) q.push(pr(t1.FR-1,t1.SE));
if (t2.FR>1) q.push(pr(t2.FR-1,t2.SE));
}
return que;
}
vector <int> e[100005];
int siz1[100005],siz2[100005],n;
int fa[100005],dep[100005];
void dfs1(int x) {
siz1[x]=1;
for(int i=0;i<e[x].size();i++)
if (e[x][i]!=fa[x]) {
int u=e[x][i];
fa[u]=x;dep[u]=dep[x]+1;
dfs1(u);
siz1[x]+=siz1[u];
siz2[x]=max(siz2[x],siz1[u]);
}
siz2[x]=max(siz2[x],n-siz1[x]);
}
int dfn[100005],ed[100005],num[100005],dfs_cnt;
int val[100005];
void dfs2(int x) {
dfn[x]=++dfs_cnt;
num[dfs_cnt]=x;
for(int i=0;i<e[x].size();i++)
if (e[x][i]!=fa[x]) dfs2(e[x][i]);
ed[x]=dfs_cnt;
}
priority_queue <pr,vector<pr>,greater<pr> > q1[100005];
queue <int> q2[100005];
void dfs3(int x) {
static int pos[100005];
for(int i=0;i<e[x].size();i++)
if (e[x][i]!=fa[x]) dfs3(e[x][i]);
vector<int> vt1;
int sz=0;
for(int i=0;i<e[x].size();i++)
if (e[x][i]!=fa[x]&&q2[e[x][i]].size()) {
int u=e[x][i];
pos[sz++]=u;
vt1.push_back(q2[u].size());
}
pos[sz++]=x;
vt1.push_back(1);
q2[x].push(x);
vector<pr> vt2=calc2(val[x],vt1);
for(int i=0;i<vt2.size();i++) {
int u=pos[vt2[i].FR],v=pos[vt2[i].SE];
printf("%d %d\n",q2[u].front(),q2[v].front());
q2[u].pop();
q2[v].pop();
}
for(int i=0;i<e[x].size();i++)
if (e[x][i]!=fa[x]) {
int u=e[x][i];
if (q2[u].size()>q2[x].size()) swap(q2[x],q2[u]);
while (!q2[u].empty()) {
q2[x].push(q2[u].front());
q2[u].pop();
}
}
}
int main() {
ll k;
scanf("%d%lld",&n,&k);
for(int i=1;i<n;i++) {
int x,y;
scanf("%d%d",&x,&y);
e[x].push_back(y);
e[y].push_back(x);
}
dfs1(1);
int rt=1;
for(int i=1;i<=n;i++)
if (siz2[i]<siz2[rt]) rt=i;
fa[rt]=dep[rt]=0;
dfs1(rt);
for(int i=1;i<=n;i++) {
val[i]=siz1[i];
for(int j=0;j<e[i].size();j++)
if (fa[e[i][j]]==i) val[i]-=(siz1[e[i][j]]>>1<<1);
val[i]>>=1;
}
ll s1=0,s2=0;
for(int i=1;i<=n;i++) {
s1+=dep[i];
if (siz1[i]<n) s2+=siz1[i];
s1-=2LL*val[i]*dep[i];
}
if (((k^s1)&1)||k<s1||k>s2) {
puts("NO");
return 0;
}
puts("YES");
dfs2(rt);
vector<int> vt1;
for(int i=0;i<e[rt].size();i++) {
int x=e[rt][i],s=0;
for(int j=dfn[x];j<=ed[x];j++) {
int u=num[j];
for(int k=1;k<=val[u];k++) {
s++;
q1[i].push(pr(dep[u],u));
}
}
vt1.push_back(siz1[x]);
}
vt1.push_back(1);
vector<int> vt2=calc1(vt1);
for(int i=0;i<vt2.size();i++) {
int x=vt2[i];
pr t=q1[x].top();q1[x].pop();
if (s1+2LL*t.FR<k) {
s1+=2LL*t.FR;
val[t.SE]--;
val[rt]++;
}
else {
int u=t.SE;
for(int j=1;j<=((k-s1)>>1);j++) u=fa[u];
val[t.SE]--;
val[u]++;
break;
}
}
dfs3(rt);
return 0;
}
/*
6 5
1 2
3 2
2 4
4 5
4 6
*/