8.21 CSP 模拟 27
晴天 - 周杰伦
故事的小黄花 从出生那年就飘着
童年的荡秋千 随记忆一直晃到现在
Re So So Si Do Si La
So La Si Si Si Si La Si La So
吹着前奏 望着天空
我想起花瓣试着掉落
为你翘课的那一天 花落的那一天
教室的那一间 我怎么看不见
消失的下雨天 我好想再淋一遍
没想到 失去的勇气我还留着
好想再问一遍 你会等待还是离开
刮风这天 我试过握着你手
但偏偏 雨渐渐 大到我看你不见
还要多久 我才能在你身边
等到放晴的那天 也许我会比较好一点
从前从前 有个人爱你很久
但偏偏 风渐渐 把距离吹得好远
好不容易 又能再多爱一天
但故事的最后
你好像还是说了 拜拜
为你翘课的那一天 花落的那一天
教室的那一间 我怎么看不见
消失的下雨天 我好想再淋一遍
没想到 失去的勇气我还留着
好想再问一遍 你会等待还是离开
刮风这天 我试过握着你手
但偏偏 雨渐渐 大到我看你不见
还要多久 我才能在你身边
等到放晴的那天 也许我会比较好一点
从前从前 有个人爱你很久
偏偏 风渐渐 把距离吹得好远
好不容易 又能再多爱一天
但故事的最后
你好像还是说了 拜拜
刮风这天 我试过握着你手
但偏偏 雨渐渐 大到我看你不见
还要多久 我才能够在你身边
等到放晴那天 也许我会比较好一点
从前从前 有个人爱你很久
但偏偏 雨渐渐 把距离吹得好远
好不容易 又能再多爱一天
但故事的最后
你好像还是说了拜
\(\text{ZZ\_zuozhe round}\)
T1 道路
原题:CodeForces-1060E Sergey and Subway *2000
答案是:
\[\sum_{u=1}^n\sum_{v=u+1}^n \left\lceil\dfrac{\mathrm{dist}(u,v)}{2}\right\rceil \]DFS 求就行了。
点击查看代码
int n;
struct edge{
int to,nxt;
}e[maxn<<1];
int head[maxn],cnt;
inline void add_edge(int u,int v){
e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;
e[++cnt].to=u,e[cnt].nxt=head[v],head[v]=cnt;
}
int siz[maxn][2];
ll sum[maxn][2],ans;
void dfs(int u,int f){
siz[u][0]=1;
for(int i=head[u],v;i;i=e[i].nxt){
v=e[i].to;
if(v==f) continue;
dfs(v,u);
int siz0=siz[v][1],siz1=siz[v][0];
ll sum0=sum[v][1],sum1=sum[v][0]+siz[v][0];
ans=(ans+sum[u][0]*siz0+sum0*siz[u][0]);
ans=(ans+sum[u][1]*siz0+sum0*siz[u][1]);
ans=(ans+sum[u][0]*siz1+sum1*siz[u][0]);
ans=(ans+sum[u][1]*siz1+sum1*siz[u][1]-1ll*siz[u][1]*siz1);
siz[u][0]+=siz0,siz[u][1]+=siz1;
sum[u][0]+=sum0,sum[u][1]+=sum1;
}
}
int main(){
freopen("road.in","r",stdin);
freopen("road.out","w",stdout);
n=read();
for(int i=1,u,v;i<n;++i){
u=read(),v=read();
add_edge(u,v);
}
dfs(1,0);
printf("%lld\n",ans);
return 0;
}
T2 集合
原题:CodeForces-979D Kuro and GCD and XOR and SUM *2200
如果没有第二条限制可以直接建 01-Trie 之后贪心,发现值域不大,因此建 \(w\) 棵 01-Trie,每棵只存储倍数,查询就是在第 \(k\) 棵查询。时间复杂度 \(O(w\sqrt{w}+q\log^2 w)\)。
点击查看代码
int q;
vector<int> D[maxn];
struct Trie{
int tot;
vector<int> tr[2],mn;
inline void build(int k){
tr[0].resize(lim/k*20);
tr[1].resize(lim/k*20);
mn.resize(lim/k*20);
mn[0]=inf;
}
inline void insert(int x){
mn[0]=min(mn[0],x);
int u=0;
for(int k=16;k>=0;--k){
if(!tr[(x>>k)&1][u]) tr[(x>>k)&1][u]=++tot,mn[tot]=x;
u=tr[(x>>k)&1][u];
mn[u]=min(mn[u],x);
}
}
inline void query(int x,int s){
int u=0,res=0;
if(mn[0]>s) return printf("-1\n"),void();
bool lim=true;
for(int k=16;k>=0;--k){
if((x>>k)&1){
if(tr[0][u]){
if(lim&&((s>>k)&1)) lim=false;
u=tr[0][u];
}
else{
u=tr[1][u];
res|=(1<<k);
}
}
else{
if(tr[1][u]&&(!lim||((s>>k)&1))&&mn[tr[1][u]]<=s){
u=tr[1][u];
res|=(1<<k);
}
else{
if(lim&&((s>>k)&1)) lim=false;
u=tr[0][u];
}
}
}
printf("%d\n",res);
}
}T[maxn];
int main(){
freopen("set.in","r",stdin);
freopen("set.out","w",stdout);
q=read();
for(int i=1;i<=lim;++i){
for(int j=1;j*j<=i;++j){
if(i%j) continue;
D[i].push_back(j);
if(j*j!=i) D[i].push_back(i/j);
}
T[i].build(i);
}
for(int i=1,opt,x,k,s;i<=q;++i){
opt=read();
if(opt==1){
x=read();
for(int d:D[x]) T[d].insert(x);
}
else{
x=read(),k=read(),s=read();
if(x%k) printf("-1\n");
else T[k].query(x,s-x);
}
}
return 0;
}
T3 科目五
原题:CodeForces-1101F Trucks and Cities *2400
发现 \(c\) 对答案的影响只是倍数,考虑 \(f_{l,r,k}\) 表示从 \(l\) 到 \(r\),中间加油 \(k\) 次的最小容量,转移有:
\[f_{l,r,k}=\min_{i=l+1}^{r-1} \{f_{l,i,k-1}+(x_r-x_i)\} \]具有决策单调性,分治预处理,复杂度 \(O(n^3\log n+m)\)。
点击查看代码
int n,m;
int x[maxn];
int f[maxn][maxn][maxn];
void solve(int L,int K,int l1,int r1,int l2,int r2){
if(l1>r1) return;
int mid1=(l1+r1)>>1;
int p;
for(int j=l2;j<=min(mid1-1,r2);++j){
if(max(f[L][j][K-1],f[j][mid1][0])<f[L][mid1][K]){
f[L][mid1][K]=max(f[L][j][K-1],f[j][mid1][0]);
p=j;
}
}
if(l1==r1) return;
solve(L,K,l1,mid1-1,l2,p),solve(L,K,mid1+1,r1,p,r2);
}
ll ans;
int main(){
freopen("drive.in","r",stdin);
freopen("drive.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=n;++i) x[i]=read();
memset(f,0x3f,sizeof(f));
for(int l=1;l<=n;++l){
for(int r=l+1;r<=n;++r){
f[l][r][0]=x[r]-x[l];
}
}
for(int k=1;k<=n-2;++k){
for(int l=1;l+k+1<=n;++l){
solve(l,k,l+k+1,n,l+k,n);
}
}
for(int i=1,s,t,c,r;i<=m;++i){
s=read(),t=read(),c=read(),r=read();
r=min(r,t-s-1);
ans=max(ans,1ll*f[s][t][r]*c);
}
printf("%lld\n",ans);
return 0;
}
T4 监狱
考虑一个出发的先后关系,对于路径 \(i\) 和 \(j\),如果 \(i\) 的起点被 \(j\) 包含,那么 \(i\) 应当在 \(j\) 之前出发;如果 \(i\) 的终点被 \(j\) 包含,那么 \(i\) 应当在 \(j\) 之后出发,建图跑拓扑排序。
考虑线段树优化建图,\(i\) 对应节点向起点 \(u_i\) 的内向树对应叶子连边,同时 \((u_i,v_i)\) 树剖得到的区间在外向树节点连向 \(i\) 对应节点;同理终点 \(v_i\) 的外向树对应对应向 \(i\) 对应节点连边,同时 \(i\) 对应节点连向 \((u_i,v_i)\) 树剖得到的区间在内向树节点。
这样建图可以使得 \(i\) 经过起点 \(u_i\) 在内向树到达包含其的所有区间并到达 \(j\),终点同理。
但这样连边天然成环,注意一个节点只会作为一条路径的起点,那么外向树连边时漏过起点,可以去掉这个环,终点同理。
时间复杂度 \(O(n\log^2 n)\)。
点击查看代码
int t;
int n,m;
vector<int> E[maxn];
int fa[maxn],dep[maxn],siz[maxn],son[maxn];
int top[maxn],dfn[maxn],dfncnt;
void dfs1(int u,int f,int d){
fa[u]=f,dep[u]=d,siz[u]=1;
int maxson=-1;
for(int v:E[u]){
if(v==f) continue;
dfs1(v,u,d+1);
siz[u]+=siz[v];
if(siz[v]>maxson) son[u]=v,maxson=siz[v];
}
}
void dfs2(int u,int t){
top[u]=t,dfn[u]=++dfncnt;
if(!son[u]) return;
dfs2(son[u],t);
for(int v:E[u]){
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
struct edge{
int to,nxt;
}e[maxn*80];
int head[maxn*9],cnt;
int deg[maxn*9];
inline void add_edge(int u,int v){
++deg[v];
e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;
}
int pos[maxn];
struct SegmentTree{
#define mid ((l+r)>>1)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
void build(int rt,int l,int r){
if(l==r) return pos[l]=rt,void();
add_edge(rt,rt<<1),add_edge(rt,rt<<1|1);
add_edge(4*n+(rt<<1),4*n+rt),add_edge(4*n+(rt<<1|1),4*n+rt);
build(lson),build(rson);
}
void update(int rt,int l,int r,int p,int pl,int pr){
if(pl<=l&&r<=pr){
add_edge(p,rt);
add_edge(4*n+rt,p);
return;
}
if(pl<=mid) update(lson,p,pl,pr);
if(pr>mid) update(rson,p,pl,pr);
}
void update1(int rt,int l,int r,int p,int pl,int pr){
if(pl>pr) return;
if(pl<=l&&r<=pr){
add_edge(p,rt);
return;
}
if(pl<=mid) update1(lson,p,pl,pr);
if(pr>mid) update1(rson,p,pl,pr);
}
void update2(int rt,int l,int r,int p,int pl,int pr){
if(pl>pr) return;
if(pl<=l&&r<=pr){
add_edge(4*n+rt,p);
return;
}
if(pl<=mid) update2(lson,p,pl,pr);
if(pr>mid) update2(rson,p,pl,pr);
}
#undef mid
#undef lson
#undef rson
}S;
inline void update(int u,int v,int id){
add_edge(8*n+id,4*n+pos[dfn[u]]);
add_edge(pos[dfn[v]],8*n+id);
int x=u,y=v;
while(top[u]!=top[v]){
if(dep[top[u]]>dep[top[v]]) swap(u,v);
if(v!=x&&v!=y) S.update(1,1,n,8*n+id,dfn[top[v]],dfn[v]);
else{
S.update(1,1,n,8*n+id,dfn[top[v]],dfn[v]-1);
if(v==x) add_edge(8*n+id,pos[dfn[x]]);
else add_edge(4*n+pos[dfn[y]],8*n+id);
}
v=fa[top[v]];
}
if(dep[u]>dep[v]) swap(u,v);
if(u!=y&&v!=y) S.update1(1,1,n,8*n+id,dfn[u],dfn[v]);
else if(u!=v){
if(u==y) S.update1(1,1,n,8*n+id,dfn[u]+1,dfn[v]);
else S.update1(1,1,n,8*n+id,dfn[u],dfn[v]-1);
}
if(u!=x&&v!=x) S.update2(1,1,n,8*n+id,dfn[u],dfn[v]);
else if(u!=v){
if(u==x) S.update2(1,1,n,8*n+id,dfn[u]+1,dfn[v]);
else S.update2(1,1,n,8*n+id,dfn[u],dfn[v]-1);
}
}
queue<int> q;
int main(){
freopen("prison.in","r",stdin);
freopen("prison.out","w",stdout);
t=read();
while(t--){
n=read();
cnt=0;
for(int i=1;i<=n;++i){
fa[i]=dep[i]=siz[i]=son[i]=0;
top[i]=dfn[i]=0;
E[i].clear();
}
for(int i=1,u,v;i<n;++i){
u=read(),v=read();
E[u].push_back(v),E[v].push_back(u);
}
dfs1(1,0,0);
dfs2(1,1);
m=read();
for(int i=1;i<=8*n+m;++i) head[i]=0,deg[i]=0;
dfncnt=0;
S.build(1,1,n);
for(int i=1,u,v;i<=m;++i){
u=read(),v=read();
update(u,v,i);
}
for(int i=1;i<=8*n+m;++i){
if(!deg[i]) q.push(i);
}
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u],v;i;i=e[i].nxt){
v=e[i].to;
--deg[v];
if(!deg[v]) q.push(v);
}
}
bool chk=true;
for(int i=1;i<=8*n+m;++i){
if(deg[i]){
chk=false;
break;
}
}
if(chk) printf("Yes\n");
else printf("No\n");
}
return 0;
}
反思
T2 看到值域很小没有意识到暴力建约数 01-Trie。
T3 暴力二分 \(O(nm\log w)\),\(mid\) 开 int
喜挂 \(75\)。
T4 想到要线段树优化建图,认为要对路径排序关于路径建线段树,DFS 序的优化做法应当是平凡的。
弱智。
标签:考后,int,void,tr,id,dfn,maxn,CSP,模拟 From: https://www.cnblogs.com/SoyTony/p/Simulation_Problems_of_CSP-S_in_August_8.html