8VC Venture Cup 2016 - Final Round (CF627)
A. XOR Equation
最低位没有加法进位产生的影响,考虑从低位向高位 dp。
设 \(f_{i,0/1}\) 表示正在考虑第 \(i\) 位,前 \(i-1\) 位都满足限制,有无进位的方案数。
转移的时候枚举这一位两个数分别填 \(a,b\),\(x_i\) 表示 \(x\) 在二进制下的第 \(i\) 位,有
特判一下 \(s=x\),因为这样 \(a,b\) 会有 \(0\)。
Code
#define int long long
const int N=105;
int f[N][2],s,x;
il int get(int x,int i) {return x>>i&1;}
signed main()
{
s=read(),x=read();
int n=__lg(max(x,s))+1;
f[0][0]=1;
for(int i=0;i<=n;i++)
for(int j=0;j<=1;j++)
for(int a=0;a<2;a++)
for(int b=0;b<2;b++)
{
if((a^b)!=get(x,i)) continue;
if((a+b+j)%2!=get(s,i)) continue;
f[i+1][(a+b+j)/2]+=f[i][j];
}
if(s==x) f[n][0]-=2;
printf("%lld\n",f[n][0]);
return 0;
}
B. Factory Repairs
线段树板子。
C. Package Delivery
快看,这里有 CSP J 2023 T2 原题!
直接按费用从小到大贪心就行。
D. Preorder Test
最小值最大,考虑二分答案。问题转化为判断能否使 dfs 序的前 \(k\) 位都大于等于 \(mid\)。
树形 dp,设 \(f_i\) 表示以 \(i\) 为根的子树,dfs 序最多前 \(f_i\) 个位置满足条件。
那么对于一个点 \(u\),转移是这样的:
- \(u<mid\),\(f_u\) 一定是 \(0\)。
- 否则,对于所有 \(f_v=siz_v\) 都可以加入 \(f_u\),在此基础上加 \(f_v<siz_v\) 最大的 \(f_v\)。
其实这样换根就可以了,但是没有必要。以 \(x\) 为整棵树的根的答案可以直接用已有的 dp 值得出。
Code
const int N=2e5+5;
int n,m,rt,minn=2e9,a[N];
int head[N],cnt;
struct node{
int nxt,to;
}e[N<<1];
void add(int u,int v){
e[++cnt]={head[u],v};head[u]=cnt;
}
int siz[N],f[N];
int ans=0;
void dfs(int now,int fa,int x)
{
f[now]=siz[now]=1;
int mx1=0,mx2=0;
for(int i=head[now];i;i=e[i].nxt)
{
int v=e[i].to;if(v==fa) continue;
dfs(v,now,x);
siz[now]+=siz[v];
if(f[v]==siz[v]) f[now]+=f[v];
else if(f[v]>mx1) mx2=mx1,mx1=f[v];
else if(f[v]>mx2) mx2=f[v];
}
if(a[now]<x) f[now]=0;
else f[now]+=mx1;
ans=max(ans,f[now]+mx2);
}
bool check(int x)
{
memset(siz,0,sizeof(siz));
memset(f,0,sizeof(f));ans=0;
dfs(rt,0,x);
return ans>=m;
}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++) if(a[i]<minn) minn=a[i],rt=i;
for(int i=1;i<n;i++)
{
int u=read(),v=read();
add(u,v),add(v,u);
}
int l=1,r=1e6;
while(l<r)
{
int mid=(l+r+1)>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
printf("%d\n",l);
return 0;
}
E. Orchestra
好厉害啊这个题。
首先把问题转化成总数减不合法的。发现 \(k\) 很小,考虑在这上面搞点东西。
将所有给定的点按纵坐标为第一关键字,横坐标为第二关键字排序,并重新标号。对点 \(i\) 维护 \(cnt_i\) 表示 \(i\) 往后数第 \(k\) 个点,这个点所在的纵坐标。
那么我们只要维护了 \(cnt_i\),就能求出答案。
首先枚举矩形的上边界,倒序枚举下边界,并从集合中删点。这可以使用双向链表维护,一次删点至多会对它前面的 \(k\) 个点的 \(cnt\) 值产生贡献,暴力更新他们即可。
时间复杂度 \(O(n^2k)\),但是它真的好难调。
Code
#define int long long
const int N=3005;
int n,m,t,k,tot;
struct node{int x,y;} a[N];
int b[N];
il bool cmp(node x,node y) {return x.y==y.y?x.x<y.x:x.y<y.y;}
int flag[N],vis[N],lst[N],nxt[N];
int Ans,ans,cnt[N],Cnt[N],len[N],L[N],R[N];
vector<int> T[N];
void upd(int x)
{
if(!x) return;
if(cnt[x]) ans-=Cnt[x]; int now=x;
for(int i=2;i<=k;i++) now=nxt[now];
cnt[x]=a[now].y;
Cnt[x]=(a[x].y-L[a[x].y])*(m-cnt[x]+1);
ans+=Cnt[x];
}
int solve(int st)
{
tot=0,ans=0; int Ans=0; a[t+1].y=m+1;
for(int i=1;i<=n;i++) T[i].clear();
for(int i=1;i<=m;i++) vis[i]=0,L[i]=0,R[i]=0;
for(int i=1;i<=t;i++) flag[i]=0,cnt[i]=0;
for(int i=1;i<=t;i++)
{
if(a[i].x>=st)
{
b[++tot]=i;
T[a[i].x].push_back(i);
if(!vis[a[i].y]) vis[a[i].y]=1,flag[i]=1;
}
}
int lt=0;
for(int i=1;i<=m;i++) if(vis[i]) L[i]=lt,lt=i;
lt=m+1;
for(int i=m;i;i--) if(vis[i]) R[i]=lt,lt=i;
b[tot+1]=t+1,nxt[t+1]=t+1;
b[0]=0,lst[0]=0;
for(int i=1;i<=tot;i++) lst[b[i]]=b[i-1],nxt[b[i]]=b[i+1];
for(int i=1;i<=tot;i++) if(flag[b[i]]) upd(b[i]);
for(int i=n;i>=st;i--)
{
Ans+=ans;
for(auto x:T[i])
{
if(flag[x])
{
ans-=Cnt[x],cnt[x]=m+1;
R[L[a[x].y]]=R[a[x].y],L[R[a[x].y]]=L[a[x].y];
upd(nxt[x]);
}
nxt[lst[x]]=nxt[x],lst[nxt[x]]=lst[x];
int now=x;
for(int i=2;i<=k;i++)
{
now=lst[now];
if(flag[now]) upd(now);
}
}
}
return Ans;
}
signed main()
{
n=read(),m=read(),t=read(),k=read();
for(int i=1;i<=t;i++) a[i].x=read(),a[i].y=read();
sort(a+1,a+t+1,cmp);
for(int i=1;i<=n;i++) Ans+=solve(i);
printf("%lld\n",Ans);
return 0;
}
F. Island Puzzle
考虑先将 \(0\) 移到目标位置。
手玩一下可以发现,如果不加一条边,我们把 \(0\) 移到它的目标位置上的方案是唯一确定的。因为走重复路径的话交换操作会相互抵消。
那么满足以上条件的最终局面也是唯一确定的,判断它与目标是否完全一致即可。
若不一致,则表明需要加边。加边 \((x,y)\) 后,这条边与原树上的路径形成了一个环。把 \(0\) 从根移到环上,则 \(0\) 每在这个环上绕一圈,这个环上的点循环移动一位。
对于可行性,判断初始局面与目标局面不一样的位置是否形成循环位移。
最少次数要考虑正着转和反着转的问题,同时去掉重复的路径。这一步的细节很多,建议配合代码食用。
代码中的 LCA 是不必要的,因为查询次数只有 \(O(1)\),完全可以跑暴力。这里是因为懒得写直接粘了个板子。因为一些神秘细节调了大概有 4h。
Code
const int N=2e5+5;
int n,rt,pos[N],a[N],b[N];
int dfn[N],tot,dep[N],st[20][N],Fa[N];
vector<int> e[N];
struct LCA
{
il int get(int x,int y) {return dep[x]<dep[y]?x:y;}
void dfs(int u,int fa)
{
Fa[u]=fa;
dfn[u]=++tot,st[0][tot]=fa; dep[u]=dep[fa]+1;
for(auto v:e[u]) if(v!=fa) dfs(v,u);
}
il void init()
{
dfs(rt,0);
for(int i=1;(1<<i)<=n;i++)
for(int j=1;j<=n-(1<<i)+1;j++)
st[i][j]=get(st[i-1][j],st[i-1][j+(1<<i-1)]);
}
il int lca(int x,int y)
{
if(x==y) return x;
if((x=dfn[x])>(y=dfn[y])) swap(x,y);
int l=__lg(y-x);
return get(st[l][x+1],st[l][y-(1<<l)+1]);
}
}l;
int vis[N],ct[N],ps[N],fg[N];
vector<int> s,t;
il void fail() {printf("-1\n");exit(0);}
void dfs(int u,int fa)
{
if(a[u]!=b[u]) s.push_back(a[u]),t.push_back(b[u]);
for(auto v:e[u]) if(vis[v]&&v!=fa)
{
if(dep[v]<dep[u]) fg[u]=1;
else fg[v]=-1;
dfs(v,u);
}
}
il int getdis(int x,int y)
{
return dep[x]+dep[y]-2*dep[l.lca(x,y)];
}
int main()
{
n=read();
for(int i=1;i<=n;i++) a[i]=read(),pos[a[i]]=i;
for(int i=1;i<=n;i++)
{
b[i]=read();
if(!b[i]) rt=i;
}
for(int i=1;i<n;i++)
{
int u=read(),v=read();
e[u].push_back(v),e[v].push_back(u);
}
l.init();
int lst=pos[0],ans=0;
while(pos[0]!=rt)
{
ans++;
int x=pos[0],y=Fa[x];
swap(pos[0],pos[a[y]]);
swap(a[x],a[y]);
}
int p=-1;
for(int i=1;i<=n;i++) if(b[i]!=a[i])
{
if(p==-1||dep[Fa[i]]<dep[p]) p=Fa[i];
}
if(p==-1) {printf("0 %d\n",ans);return 0;}
ans=0;
for(int i=1;i<=n;i++) if(b[i]!=a[i]) vis[i]=1;
vis[p]=1;
for(int i=1;i<=n;i++) if(b[i]!=a[i])
{
if(!vis[Fa[i]]) fail();
ct[Fa[i]]++;
}
int u=0,v=0;
for(int i=1;i<=n;i++)
{
if(ct[i]>2||(ct[i]==2&&i!=p)) fail();
if((!ct[i]||ct[i]==1&&i==p)&&vis[i]) {if(!u) u=i;else if(!v) v=i;else fail();}
}
dfs(u,0); int len=s.size(),lt=-1e9;
for(int i=0;i<len;i++) ps[s[i]]=i;
for(int i=0;i<len;i++)
if(i==0) lt=ps[t[i]]-i>=0?ps[t[i]]-i:ps[t[i]]-i+len;
else if((ps[t[i]]-i>=0?ps[t[i]]-i:ps[t[i]]-i+len)!=lt) fail();
int dis=0,now=lst; lt=len-lt;
while(now)
{
if(fg[now]==1) dis++;
else if(fg[now]==-1) dis--;
now=Fa[now];
}
if(!dis)
{
ans=min(lt,len-lt)*(len+1)+dep[lst]-1+2*(dep[p]-1)-2*(dep[l.lca(lst,p)]-1);
}
else if(dis>0) ans=dep[lst]-1+min(lt*(len+1)-2*dis,(len-lt)*(len+1));
else
{
dis=-dis,ans=dep[lst]-1+min(lt*(len+1),(len-lt)*(len+1)-2*dis);
}
printf("%d %d %d\n",u,v,ans);
}
本文来自博客园,作者:樱雪喵,转载请注明原文链接:https://www.cnblogs.com/ying-xue/p/cf627.html