A - CF566E Restoring Map
Sol
对每一对 ( u , v ) (u,v) (u,v),求出包含它的 s e t set set的数量是等于 0 , 1 , 2 0,1,2 0,1,2还是大于等于 3 3 3。
如果为 0 0 0则这两个点之间的距离大于 4 4 4,等于 1 1 1则距离等于 4 4 4,等于 2 2 2则距离等于 3 3 3,大于 2 2 2则距离小于等于 2 2 2。
观察发现,如果图的直径大于等于 5 5 5,我们就可以通过已经知道的距离为 3 3 3和距离为 4 4 4的点对,确定下两个点之间的距离的奇偶性,然后结合已知的距离小于等于 2 2 2的点对就可以知道哪些点对之间的距离是 1 1 1。
然后再讨论直径小于 5 5 5的情况即可。
Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <assert.h>
#define PII pair<int,int>
#define MP make_pair
#define fir first
#define sec second
#define PB push_back
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
x=0; char c=getchar(); int f=1;
while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
const int N=1010;
int nxt[N][N],num[N][N],n;
int find(int p,int x) { return nxt[p][x]==x?x:nxt[p][x]=find(p,nxt[p][x]); }
int b[N],vis[N];
vector<PII> G[N];
int col[N];
void dfs(int u,int c) {
col[u]=c;
for(int i=0;i<G[u].size();++i) {
int v=G[u][i].fir;
if(!~col[v]) dfs(v,c^G[u][i].sec);
}
}
int main() {
rd(n);
for(int i=1;i<=n;++i) {
for(int j=1;j<=n+1;++j)
nxt[i][j]=j;
}
for(int i=1;i<=n;++i) {
int m; rd(m);
for(int j=1;j<=m;++j) rd(b[j]),vis[b[j]]=1;
for(int j=1;j<=m;++j) {
int u=b[j];
// for(int k=1;k<=n;++k) if(vis[k]&&num[u][k]<3) num[u][k]++;
for(int k=find(u,1);k<=n;k=find(u,k+1)) if(vis[k]) {
num[u][k]++;
if(num[u][k]==3) nxt[u][k]=k+1;
}
}
for(int j=1;j<=m;++j) vis[b[j]]=0;
}
if(n==2) {
printf("1 2\n");
return 0;
}
int f5=0;
for(int i=1;i<=n&&!f5;++i) for(int j=1;j<=n&&!f5;++j)
if(num[i][j]==0) f5=1;
if(!f5) {
int f4=0;
for(int i=1;i<=n&&!f4;++i) for(int j=1;j<=n&&!f4;++j)
if(num[i][j]==1) f4=1;
if(!f4) {
int f3=0;
for(int i=1;i<=n&&!f3;++i) for(int j=1;j<=n&&!f3;++j)
if(num[i][j]==2) f3=1;
if(!f3) {
for(int i=2;i<=n;++i) printf("1 %d\n",i);
return 0;
}
int u=0,v=0;
for(int i=1;i<=n;++i) {
int flg=0;
for(int j=1;j<=n;++j) if(num[i][j]==2) { flg=1; break; }
if(!flg) {
if(!u) u=i;
else { v=i; break; }
}
}
int p=1; for(;p==u||p==v;++p);
printf("%d %d\n",u,v);
printf("%d %d\n",p,u);
for(int i=1;i<=n;++i) if(i!=u&&i!=v&&i!=p) {
if(num[i][p]==2) printf("%d %d\n",v,i);
else printf("%d %d\n",u,i);
}
return 0;
}
int rt=1;
for(;;++rt) {
int flg=1;
for(int i=1;i<=n;++i)
if(i!=rt&&num[i][rt]<3) { flg=0; break; }
if(flg) break;
}
vector<int> a,b;
for(int i=1;i<=n;++i) if(i!=rt) {
int mi=3;
for(int j=1;j<=n;++j) mi=min(mi,num[i][j]);
if(mi==2) a.PB(i);
else b.PB(i);
}
for(int i=0;i<a.size();++i) printf("%d %d\n",a[i],rt);
for(int i=0;i<b.size();++i)
for(int j=0;j<a.size();++j)
if(num[b[i]][a[j]]==3) { printf("%d %d\n",b[i],a[j]); break; }
return 0;
}
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j) if(num[i][j]&&num[i][j]<3) {
if(num[i][j]==1) G[i].PB(MP(j,0)),G[j].PB(MP(i,0));
else G[i].PB(MP(j,1)),G[j].PB(MP(i,1));
}
memset(col,-1,sizeof(col));
int tot=0;
for(int i=1;i<=n;++i) if(!~col[i]) dfs(i,tot),tot+=2;
int edgetot=0;
for(int i=1;i<=n;++i) for(int j=i+1;j<=n;++j)
if(num[i][j]==3&&(col[i]>>1)==(col[j]>>1)&&col[i]!=col[j]) printf("%d %d\n",i,j),edgetot++;
return 0;
}
B - AGC034E Complete Compress
题意
有一棵包含 n n n个节点的树,以及一个长度为 n n n的 01 01 01串,第 i i i个字符表示第 i i i个节点上的碎片的数量。一次操作你可以选择一个之间距离(路径上的边数)至少是 2 2 2的两个点,把它们向彼此移动一个节点。问最少需要多少次操作使得所有的碎片集中在同一个点。如果不可能则输出 − 1 -1 −1。 n ≤ 2000 n\le 2000 n≤2000
Sol
首先枚举一个点作为最终停留的点,把这个点作为根。
那么只会有两种类型的操作:1)将这个根的不同子树内的两个碎片移动。2)将这个根的某个子树内、 l c a lca lca不为它们其中之一的两个碎片移动。实际上对于任意一个节点,它的子树内的操作也一定满足。
考虑对一个点求出这个点子树内的碎片进行操作过后,到这个点的父亲的距离之和的最小值;求出对这个子树内的碎片到这个点的父亲的距离之和;分别记为 m i n u min_u minu和 m a x u max_u maxu。设 u u u节点的所有儿子中, m i n v min_v minv最大的儿子为 p p p。那么有$min_u = \max { 0 , \min_p - (\max_u - \max_p)} + size_u , 其 中 ,其中 ,其中size_u 表 示 表示 表示u 子 树 内 的 碎 片 的 数 量 。 最 后 , 判 断 根 节 点 的 子树内的碎片的数量。最后,判断根节点的 子树内的碎片的数量。最后,判断根节点的\min$是否为 0 0 0,如果是,说明存在合法的操作方式,操作次数为 max r t 2 \max_{rt}\over 2 2maxrt。
Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define PII pair<int,int>
#define MP make_pair
#define fir first
#define sec second
#define PB push_back
#define db long double
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
x=0; char c=getchar(); int f=1;
while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
const int N=2010;
int head[N],ecnt,n;
struct ed { int to,next; }e[N<<1];
void ad(int x,int y) {
e[++ecnt]=(ed){y,head[x]}; head[x]=ecnt;
e[++ecnt]=(ed){x,head[y]}; head[y]=ecnt;
}
int curd;
char str[N];
void dfs(int u,int last,int dis) {
if(str[u]=='1') curd+=dis;
for(int k=head[u];k;k=e[k].next) {
int v=e[k].to; if(v==last) continue;
dfs(v,u,dis+1);
}
}
int ans=1e9;
int tot;
void dfs1(int u,int last,int d) {
if(str[u]=='1') tot+=d;
for(int k=head[u];k;k=e[k].next) {
int v=e[k].to; if(v==last) continue;
dfs1(v,u,d+1);
}
}
int mi[N],mx[N],sz[N];
void dfs2(int u,int last) {
int tot=0,p=0;
sz[u]=str[u]-'0';
for(int k=head[u];k;k=e[k].next) {
int v=e[k].to; if(v==last) continue;
dfs2(v,u);
if(mi[v]>mi[p]) p=v;
tot+=mx[v];
sz[u]+=sz[v];
}
mx[u]=tot;
tot-=mx[p];
mi[u]=max(0,mi[p]-tot);
if(last) mi[u]+=sz[u],mx[u]+=sz[u];
}
void sol(int u) {
tot=0,dfs1(u,0,0);
if(tot&1) return;
dfs2(u,0);
if(mi[u]>0) return;
ans=min(ans,tot/2);
}
int main() {
rd(n); scanf("%s",str+1);
for(int i=1,x,y;i<n;++i) rd(x),rd(y),ad(x,y);
for(int i=1;i<=n;++i) sol(i);
if(ans==1e9) printf("-1");
else printf("%d",ans);
return 0;
}
C - AGC022D Shopping
Sol
O ( n 2 ) O(n^2) O(n2)的暴力是考虑每个购物中心是右进右出/左进右出/右进左出/左进左出,进行动态规划。但是这与正解没有什么关系。
首先可以把 ⌊ t i 2 L ⌋ \lfloor {t_i\over 2L}\rfloor ⌊2Lti⌋加入答案,然后把 t i t_i ti模 2 L 2L 2L。
我们要求的实际上是列车至少跑多少个来回。
对于第 i i i个购物中心,记 l i l_i li表示从右边过来到 i i i,列车下次经过 i i i的时候能不能直接上车; r i r_i ri同理。
对于最右边的那个购物中心,我们一定会从左边到它,然后从它到左边。所以如果 r n = 0 r_n=0 rn=0则 a n s + + ans++ ans++。
对于剩下的购物中心,如果有 i < j , l i = r j = 1 i< j,l_i=r_j=1 i<j,li=rj=1,则可以把它们匹配起来,然后少跑一个来回。由于所有 l i = 1 , r i = 0 l_i=1,r_i=0 li=1,ri=0的点一定在所有 l i = 0 , r i = 1 l_i=0,r_i=1 li=0,ri=1的点的右边,可以单独考虑两边的匹配。
如果有一个购物中心的 t i = 0 t_i=0 ti=0,或者是 l i = r i = 0 l_i=r_i=0 li=ri=0,那么直接把它的贡献加入答案,不用考虑用它匹配。因为 t i = 0 t_i=0 ti=0的时候只需要保证经过了它就可以了,另一种情况由于必须在 i i i等列车跑完一圈所以与 t i = 0 t_i=0 ti=0是等价的。
Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#define PB push_back
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
x=0; char c=getchar(); int f=1;
while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
const int N=3e5+10;
vector<int> s;
ll xi[N],ti[N];
int li[N],ri[N],n;
int vis[N];
int stk[N],top;
ll ans,L;
int main() {
rd(n),rd(L);
ans=n;
for(int i=1;i<=n;++i) rd(xi[i]);
for(int i=1;i<=n;++i) rd(ti[i]);
for(int i=1;i<=n;++i) {
ans+=ti[i]/(2*L);
ti[i]%=2*L;
if(ti[i]==0) ans--,vis[i]=1;
else {
li[i]=2*xi[i]>=ti[i];
ri[i]=2*(L-xi[i])>=ti[i];
if(!li[i]&&!ri[i]) ti[i]=0,vis[i]=1;
}
// printf("%d:(%d,%d) %d\n",i,li[i],ri[i],vis[i]);
}
int px=1;
while(px<n&&!(li[px]==1&&ri[px]==0)) px++;
int tot=0,top=0;
for(int i=1;i<px;++i) if(!vis[i]) {
if(!li[i]) {
if(top) top--,ans--;
}
else top++;
}
tot+=top,top=0;
for(int i=n-1;i>=px;--i) if(!vis[i]) {
if(!ri[i]) {
if(top) top--,ans--;
}
else top++;
}
tot+=top;
ans-=tot>>1;
if(!ri[n]) ans++;
printf("%lld",ans*2*L);
return 0;
}
0;
for(int i=1;i<px;++i) if(!vis[i]) {
if(!li[i]) {
if(top) top--,ans--;
}
else top++;
}
tot+=top,top=0;
for(int i=n-1;i>=px;--i) if(!vis[i]) {
if(!ri[i]) {
if(top) top--,ans--;
}
else top++;
}
tot+=top;
ans-=tot>>1;
if(!ri[n]) ans++;
printf("%lld",ans*2*L);
return 0;
}