test201909027 老Z
30+100+40=170。数据出锅*2,也是没谁了。
装饰
快要到 Mope 的生日了,Mope 希望举行一场盛大的生日派对。
派对的准备工作中当然有装饰房间啦。Mope 的房间里有按从左到右的顺序排好的 n 个装饰品,其中第 i 个装饰品被挂在高为 ai 的地方。Mope 不希望改变它们的位置,所以他决定取下一些饰品,设留下来的序列为{bi},他希望任意相邻三项 bi, bi+1, bi+2 满足bi ≤ bi+1 ≤ bi+2或者bi ≥ bi+1 ≥ bi+2。Mope 希望知道他至少要取下多少个饰品。
对于所有测试点:1 ≤ n ≤ 200,000, 1 ≤ ai ≤ 1,000,000,000
题解
维护三种 DP 数组,fi 表示以高度 i 为结尾的最后两项是严格上升的最长子序列长度,gi 表示以高度 i 为结尾的最后两项是严格下降的最长子序列长度,hi 表示以高度 i 为结尾的最后两项是严格相等的最长子序列长度。
转移十分显然,线段树维护即可。时间复杂度 O(n log n)。
co int N=200000+10;
int a[N],b[N];
int f[N*4],g[N*4],h[N*4];
#define lc (x<<1)
#define rc (x<<1|1)
void chgf(int x,int l,int r,int p,int v){
if(l==r){
f[x]=max(f[x],v);
return;
}
int mid=(l+r)>>1;
if(p<=mid) chgf(lc,l,mid,p,v);
else chgf(rc,mid+1,r,p,v);
f[x]=max(f[lc],f[rc]);
}
void chgg(int x,int l,int r,int p,int v){
if(l==r){
g[x]=max(g[x],v);
return;
}
int mid=(l+r)>>1;
if(p<=mid) chgg(lc,l,mid,p,v);
else chgg(rc,mid+1,r,p,v);
g[x]=max(g[lc],g[rc]);
}
void chgh(int x,int l,int r,int p,int v){
if(l==r){
h[x]=max(h[x],v);
return;
}
int mid=(l+r)>>1;
if(p<=mid) chgh(lc,l,mid,p,v);
else chgh(rc,mid+1,r,p,v);
h[x]=max(h[lc],h[rc]);
}
int qryf(int x,int l,int r,int ql,int qr){
if(ql<=l and r<=qr) return f[x];
int mid=(l+r)>>1;
if(qr<=mid) return qryf(lc,l,mid,ql,qr);
if(ql>mid) return qryf(rc,mid+1,r,ql,qr);
return max(qryf(lc,l,mid,ql,qr),qryf(rc,mid+1,r,ql,qr));
}
int qryg(int x,int l,int r,int ql,int qr){
if(ql<=l and r<=qr) return g[x];
int mid=(l+r)>>1;
if(qr<=mid) return qryg(lc,l,mid,ql,qr);
if(ql>mid) return qryg(rc,mid+1,r,ql,qr);
return max(qryg(lc,l,mid,ql,qr),qryg(rc,mid+1,r,ql,qr));
}
int qryh(int x,int l,int r,int ql,int qr){
if(ql<=l and r<=qr) return h[x];
int mid=(l+r)>>1;
if(qr<=mid) return qryh(lc,l,mid,ql,qr);
if(ql>mid) return qryh(rc,mid+1,r,ql,qr);
return max(qryh(lc,l,mid,ql,qr),qryh(rc,mid+1,r,ql,qr));
}
int main(){
freopen("decoration.in","r",stdin),freopen("decoration.out","w",stdout);
int n=read<int>();
for(int i=1;i<=n;++i) b[i]=read(a[i]);
sort(b+1,b+n+1);
int m=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;++i){
// cerr<<"calc "<<i<<endl;
a[i]=lower_bound(b+1,b+m+1,a[i])-b;
int tmpf=a[i]>1?max(qryf(1,1,m,1,a[i]-1),qryh(1,1,m,1,a[i]-1)):0;
int tmpg=a[i]<m?max(qryg(1,1,m,a[i]+1,m),qryh(1,1,m,a[i]+1,m)):0;
int tmph=max(qryf(1,1,m,a[i],a[i]),max(qryg(1,1,m,a[i],a[i]),qryh(1,1,m,a[i],a[i])));
// cerr<<" f="<<tmpf<<" g="<<tmpg<<" h="<<tmph<<endl;
chgf(1,1,m,a[i],tmpf+1);
chgg(1,1,m,a[i],tmpg+1);
chgh(1,1,m,a[i],tmph+1);
}
int ans=max(qryf(1,1,m,1,m),max(qryg(1,1,m,1,m),qryh(1,1,m,1,m)));
// cerr<<"ans="<<ans<<endl;
printf("%d\n",n-ans);
return 0;
}
大样例是错的导致我调了一个半小时,后来写了个对拍才逐渐怀疑std出锅。
最短路径
Mope 做了一个梦。
在梦中,Mope 在一座沼泽地里。沼泽地里有 n 个村落,由 m 座木桥连接并连通。他发现自己的手臂上出现了一个数字,这个数字一开始是 0。每一座木桥上也有一个数字 wi。当 Mope 经过第 i 座桥时,他手臂上的数字会变成和 wi做按位或的结果。Mope 现在在 1 号村落,村落里的长老告诉他在 n 号村落里有一个魔法宝箱,他手臂上的数字越大,宝箱里的财宝就越少。Mope 希望知道当他到达 n 号村落时,手臂上的数字最小能是多少。
对于所有测试点:1 ≤ n ≤ 100,000, n − 1 ≤ m ≤ min(n(n−1)/2 , 200,000), 0 ≤wi < 230
题解
按位贪心,并查集判连通性即可。时间复杂度 O(n log w)。
co int N=100000+10;
struct edge {int u,v,w;}e[2*N];
int fa[N];
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
int main(){
freopen("path.in","r",stdin),freopen("path.out","w",stdout);
int n=read<int>(),m=read<int>();
for(int i=1;i<=m;++i) read(e[i].u),read(e[i].v),read(e[i].w);
int ans=0;
for(int c=29;c>=0;--c){
for(int i=1;i<=n;++i) fa[i]=i;
for(int i=1;i<=m;++i)if((e[i].w>>c|ans>>c)==ans>>c){
int fu=find(e[i].u),fv=find(e[i].v);
if(fu!=fv) fa[fu]=fv;
}
if(find(1)!=find(n)) ans^=1<<c;
}
printf("%d\n",ans);
return 0;
}
繁衍
2077 年,生物研究者们发现了一种神奇的生物——Mebius。
Mebius 是一种单细胞生物,生殖方式为分裂生殖。目前研究者们已发现了Mebius 的 n 个亚种,分别标号为 1~n。奇特的是,某个亚种的 Mebius 可能分裂成其他亚种的 Mebius。
Mope 是一名生物研究员,他发现 1 类亚种 Mebius 潜藏着巨大的医学价值。为了一探究竟,他需要大量的 1 类亚种 Mebius。他现在拥有一些不同种类的Mebius,他拥有大量的某种分裂促进因子可以使 Mebius 立即分裂。我们可以认为 Mope 拥有几乎无限量的分裂促进因子。Mope 想知道他最多能得到多少 1 类Mebius。
对于所有测试点:1 ≤ n ≤ 100,000, ∑ki ≤ 500,000, 0 ≤ ci ≤ 109
题解
先去掉到不了 1 的点,然后就考虑一下环的情况就行了。
容易发现若 ci>0,那么只能存在一个包含 1 的简单环。若有 ci=0,那么情况要复杂些,用 Tarjan 将 SCC 缩点后原图变成 DAG,那么对于一个环如果能到它的点和它自己的点的点权和是 0 的话它也是合法的。
这些都可以用 Tarjan 和拓扑排序判断。时间复杂度 O(n+m)。
co int N=100000+10;
int cnt[N];
vector<int> to[N],re[N];
int vis[N];
vector<int> scc[N];
int bl[N],tot;
int pos[N],low[N],dfn,st[N],top,ins[N];
void tarjan(int x,int fa){
pos[x]=low[x]=++dfn;
st[++top]=x,ins[x]=1;
for(int i=0;i<(int)re[x].size();++i){
int y=re[x][i];
if(!pos[y]){
tarjan(y,x);
low[x]=min(low[x],low[y]);
}
else if(ins[y]) low[x]=min(low[x],pos[y]);
}
if(low[x]==pos[x]){
++tot;
do{
int y=st[top];
ins[y]=0;
bl[y]=tot,scc[tot].push_back(y);
}while(st[top--]!=x);
}
}
LL scnt[N];
vector<int> sto[N];
int svis[N];
void build(int x){
svis[x]=1;
for(int i=0;i<(int)sto[x].size();++i){
int y=sto[x][i];
if(svis[y]){
scnt[x]+=scnt[y]; // edit 1
continue;
}
build(y);
scnt[x]+=scnt[y];
}
if(scnt[x])for(int i=0;i<(int)scc[x].size();++i) vis[scc[x][i]]=1;
}
int deg[N];
deque<int> Q;
int cir[N],len;
void dfs(int x){
vis[x]=2,cir[++len]=x;
for(int i=0;i<(int)to[x].size();++i){
int y=to[x][i];
if(vis[y]==1) dfs(y);
}
}
int main(){
freopen("multiplication.in","r",stdin),freopen("multiplication.out","w",stdout);
read<int>();
int n=read<int>();
for(int x=1;x<=n;++x){
for(int k=read<int>();k--;){
int y=read<int>();
to[x].push_back(y),re[y].push_back(x);
}
}
for(int i=1;i<=n;++i) read(cnt[i]);
tarjan(1,0);
for(int i=1;i<=tot;++i)
for(int j=0;j<(int)scc[i].size();++j){
int x=scc[i][j];
scnt[i]+=cnt[x];
for(int k=0;k<(int)re[x].size();++k){
int y=re[x][k];
if(i!=bl[y]) sto[i].push_back(bl[y]);
}
}
build(bl[1]);
for(int x=1;x<=n;++x)if(vis[x])
for(int i=0;i<(int)to[x].size();++i){
int y=to[x][i];
if(vis[y]) ++deg[y];
}
for(int x=1;x<=n;++x)if(vis[x] and !deg[x]) Q.push_back(x);
while(Q.size()){
int x=Q.front();
vis[x]=2,Q.pop_front();
for(int i=0;i<(int)to[x].size();++i){
int y=to[x][i];
if(vis[y]){
cnt[y]=add(cnt[y],cnt[x]);
if(--deg[y]==0) Q.push_back(y);
}
}
}
int valid_deg=1,cir_siz=0;
for(int i=1;i<=n;++i)if(vis[i]==1){
if(deg[i]>1) {valid_deg=0;break;}
++cir_siz;
}
if(cir_siz) dfs(1);
if(!valid_deg or cir_siz!=len) {puts("Infinity");return 0;}
int ans=0;
if(cir_siz)for(int i=1;i<=len;++i) ans=add(ans,cnt[cir[i]]);
else ans=cnt[1];
printf("%d\n",ans);
return 0;
}
这玩意真不适合断断续续写代码,一个小错调一下午。