noip模拟17[世界线·时间机器·weight]
noip模拟17 solutions
从开始考我就意识到这次考不好了,为啥呢?因为正常考试我连交题的欲望都没有
一共交了\(4\)遍,果不其然,\(30pts\)非常愉快的拿到了第二题的暴力分
最大的错误就是两个小时的时候,我连电脑都没碰一下,说白了还是时间分配问题,下次注意
记录一下,JYXHYX又考场A题了,我天呐,太强了
·
T1 世界线
不得不说,这个题吧,让人很有做它的欲望,但是你做着做着就觉得好像完全没有思路,
就这个,我一会想着要用并查集,一会想着要递推,一会又想着要\(dfs\),到最后也没想到正解
但是打暴力还是很重要的,这个题本身就是一个\(bitset\)优化的\(dfs\),万能的\(STL!!!\)
仔细研究研究题意你就会发现,这个连边是有传递性的,一个点会和他的所有后代都连上,他要连的边,就是他的后代数减去他的出度
所以我们如何维护他有多少后代呢,其实这个题dfs完全可以解决,完全不需要拓扑排序,
我们知道\(bitset\)可以进行基本的位运算,还可以统计1的个数,所以我只要把所有的儿子标记为1,然后不停向上合并就好了
\(bitset\)中存的就是,当前的所有后代
但是我们发现,我们根本无法存下\(N*N\)的\(bitset\),即使它一位只占用1/8字节,也就是一字,也就是int的1/32;
那我们就分成10次来算,因为你发现每一个之间是互不影响的
AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=6e4+50;
const int M=1e5+5;
int n,m,l,r;
int to[M],nxt[M],head[N],rp;
void add_edg(int x,int y){
to[++rp]=y;
nxt[rp]=head[x];
head[x]=rp;
}
bitset<N/10> b[N];
int ans[N],cdu[N],rdu[N];
bool vis[N];
void dfs(int x){
//cout<<x<<endl;
vis[x]=true;
for(re i=head[x];i;i=nxt[i]){
int y=to[i];
if(y>=l&&y<=r)b[x].set(y-l);
if(vis[y]==false)dfs(y);
b[x]=b[x]|b[y];
}
//ans[x]+=b[x].count();
//cout<<x<<" "<<ans[x]<<endl;
}
int anss;
signed main(){
scanf("%d%d",&n,&m);
for(re i=1,x,y;i<=m;i++){
scanf("%d%d",&x,&y);
add_edg(x,y);
rdu[y]++;cdu[x]++;
}
l=1,r=min(6000,n);
while(l<=n){
//cout<<l<<" "<<r<<endl;
memset(vis,false,sizeof(vis));
for(re i=1;i<=n;i++)b[i].reset();
for(re i=1;i<=n;i++)if(!rdu[i])dfs(i);//cout<<i<<endl;
for(re i=1;i<=n;i++)ans[i]+=b[i].count();
l=r+1;r=min(l+6000-1,n);
}
for(re i=1;i<=n;i++){
anss+=(ans[i]-cdu[i]);
//cout<<i<<" "<<ans[i]<<" "<<cdu[i]<<endl;
}
printf("%d",anss);
}
·
T2 时间机器
这个明显一眼就是贪心,是的没错
而我,考场上想到贪心却不知道怎么维护,然后就直接跳过了,所以没拿分;
他们说这个是最经典的覆盖问题
所以我们按照左端点右端点,双关键字排序,然后插入到set中
这样我们每次都可以查询左端点在左边,右端点距离它最近的了
注意set只按照右端点排序,就是重载运算符只比较右端点,左端点在插入时就维护好了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=5e4+5;
int T,n,m;
struct node{
int l,r,k,id;
bool operator < (node x)const{
return x.r>r;
}
}dot[N],omu[N];
set<node> st;
set<node>::iterator it,pd;
int szd[N],szo[N];
int cnt;
node ji[N];
bool cmp(node x,node y){
return x.l<y.l;
}
signed main(){
scanf("%d",&T);
while(T--){
st.clear();
scanf("%d%d",&n,&m);
for(re i=1;i<=n;i++){
scanf("%d%d%d",&dot[i].l,&dot[i].r,&dot[i].k);
dot[i].id=i;szd[i]=dot[i].k;
}
for(re i=1;i<=m;i++){
scanf("%d%d%d",&omu[i].l,&omu[i].r,&omu[i].k);
omu[i].id=i;szo[i]=omu[i].k;
}
sort(dot+1,dot+n+1,cmp);
//cout<<dot[1].r<<endl;
sort(omu+1,omu+m+1,cmp);
int j=1,flag=0;
for(re i=1;i<=n;i++){
cnt=0;
for(;j<=m&&omu[j].l<=dot[i].l;j++){
pd=st.find(omu[j]);
if(pd!=st.end()){
szo[(*pd).id]+=szo[omu[j].id];
continue;
}
st.insert(omu[j]);
}
//cout<<j<<endl;
pd=st.find(dot[i]);
if(pd!=st.end()){
if(szo[(*pd).id]>=szd[dot[i].id]){
szo[(*pd).id]-=szd[dot[i].id];
szd[dot[i].id]=0;
if(szo[(*pd).id]==0)st.erase((*pd));
continue;
}
szd[dot[i].id]-=szo[(*pd).id];
szo[(*pd).id]=0;
st.erase((*pd));
}
st.insert(dot[i]);
it=st.find(dot[i]);
while(szd[dot[i].id]){
it++;//cout<<(*it).r<<" "<<(*it).id<<endl;
if(it==st.end()){
flag=1;break;
}
if(szd[dot[i].id]>=szo[(*it).id]){
szd[dot[i].id]-=szo[(*it).id];
szo[(*it).id]=0;
ji[++cnt]=(*it);
}
else{
szo[(*it).id]-=szd[dot[i].id];
szd[dot[i].id]=0;
}
}
if(flag==1){
printf("No\n");break;
}
st.erase(dot[i]);
for(re i=1;i<=cnt;i++)st.erase(ji[i]);
}
if(!flag)printf("Yes\n");
}
}
·
T3 weight
所以这个题真的是最小生成树
然后听到这个消息之后,我没看题解就吧这个题A掉了,我太强了
哈哈哈,就是我们先找到最小生成树,记录那些是最小生成树中的边,哪些是非树边
对最小生成树进行树链剖分,我们利用非树边在他两个端点之间的路径上跑
更新树边的最大值,反过来继续更新自己
判断是否和1联通在最小生成树的时候用并查集维护就好啦
AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=7e5+5;
const int M=1e6+5;
const int inf=0x3f3f3f3f;
int n,m,ans[N];
int to[M*2],nxt[M*2],val[M*2],id[M*2],head[N],rp;
void add_edg(int x,int y,int z,int i){
to[++rp]=y;
val[rp]=z;
id[rp]=i;
nxt[rp]=head[x];
head[x]=rp;
}
struct EDGE{
int fr,to,val,id,typ;
bool operator < (EDGE x)const{
return x.val>val;
}
}edg[M];
int fai[N],si[N],rt,sz;
int find(int x){return x==fai[x]?x:fai[x]=find(fai[x]);}
void klus(){
sort(edg+1,edg+m+1);
for(re i=1;i<=n;i++)fai[i]=i,si[i]=1;
int sum=0;
for(re i=1;i<=m;i++){
int x=edg[i].fr,y=edg[i].to;
int fx=find(x),fy=find(y);
if(fx==fy)continue;
fai[fx]=fy;sum++;si[fy]+=si[fx];
edg[i].typ=1;
add_edg(x,y,edg[i].val,edg[i].id);
add_edg(y,x,edg[i].val,edg[i].id);
//if(sum==n-1)break;
}
rt=find(1);sz=si[rt];
}
int pval[N],idf[N];
int ifd[N];
struct seg_tree{
#define ls x<<1
#define rs x<<1|1
int maxn[N*4],laz[N*4],va[N*4];
/*seg_tree(){
memset(maxn,0x3f,sizeof(maxn));
memset(laz,0x3f,sizeof(laz));
}*/
void build(int x,int l,int r){
maxn[x]=inf;laz[x]=inf;
if(l==r){
va[x]=pval[ifd[l]];
return ;
}
int mid=l+r>>1;
build(ls,l,mid);
build(rs,mid+1,r);
va[x]=max(va[ls],va[rs]);
return ;
}
inline void pushdown(int x){
if(laz[x]==inf)return ;
laz[ls]=min(laz[ls],laz[x]);
maxn[ls]=min(maxn[ls],laz[x]);
laz[rs]=min(laz[rs],laz[x]);
maxn[rs]=min(maxn[rs],laz[x]);
laz[x]=inf;
return ;
}
void update(int x,int l,int r,int ql,int qr,int v){
if(ql>qr)return ;
if(ql<=l&&r<=qr){
laz[x]=min(laz[x],v);
maxn[x]=min(maxn[x],v);
return ;
}
pushdown(x);
int mid=l+r>>1;
if(ql<=mid)update(ls,l,mid,ql,qr,v);
if(qr>mid)update(rs,mid+1,r,ql,qr,v);
return ;
}
int query(int x,int l,int r,int pos){
if(l==r)return maxn[x]==inf?-1:maxn[x];
pushdown(x);
int mid=l+r>>1;
if(pos<=mid)return query(ls,l,mid,pos);
else return query(rs,mid+1,r,pos);
}
int query_re(int x,int l,int r,int ql,int qr){
if(ql>qr)return 0;
if(ql<=l&&r<=qr)return va[x];
int mid=l+r>>1,ret=0;
if(ql<=mid)ret=max(ret,query_re(ls,l,mid,ql,qr));
if(qr>mid)ret=max(ret,query_re(rs,mid+1,r,ql,qr));
return ret;
}
#undef ls
#undef rs
}xds;
int dfn[N],cnt,fa[N];
int siz[N],son[N],rak[N],top[N],dep[N];
void dfs1(int x){
siz[x]=1;son[x]=0;
for(re i=head[x];i;i=nxt[i]){
int y=to[i];
if(y==fa[x])continue;
fa[y]=x;dep[y]=dep[x]+1;
dfs1(y);siz[x]+=siz[y];
if(!son[x]||siz[y]>siz[son[x]])son[x]=y,rak[x]=i;
}
}
void dfs2(int x,int f){
dfn[x]=++cnt;top[x]=f;
ifd[cnt]=x;
if(son[x]){
pval[son[x]]=val[rak[x]];
idf[id[rak[x]]]=son[x];
dfs2(son[x],f);
}
for(re i=head[x];i;i=nxt[i]){
int y=to[i];
if(y==son[x]||y==fa[x])continue;
pval[y]=val[i];idf[id[i]]=y;
dfs2(y,y);
}
}
void change(int x,int y,int v,int i){
if(top[x]==0||top[y]==0)return ;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
xds.update(1,1,cnt,dfn[top[x]],dfn[x],v);
ans[i]=max(ans[i],xds.query_re(1,1,cnt,dfn[top[x]],dfn[x])-1);
x=fa[top[x]];
}
//if(x==y)return ;
if(dep[x]<dep[y])swap(x,y);
//cout<<dep[x]<<" "<<dfn[x]<<" "<<dep[y]<<" "<<dfn[y]<<endl;
xds.update(1,1,cnt,dfn[y]+1,dfn[x],v);
ans[i]=max(ans[i],xds.query_re(1,1,cnt,dfn[y]+1,dfn[x])-1);
}
bool cmp(EDGE x,EDGE y){
return x.id<y.id;
}
signed main(){
int ooo;scanf("%d%d%d",&n,&m,&ooo);
for(re i=1;i<=m;i++){
scanf("%d%d%d",&edg[i].fr,&edg[i].to,&edg[i].val);
edg[i].id=i;
}
klus();dep[1]=1;
dfs1(1);dfs2(1,1);
xds.build(1,1,cnt);
sort(edg+1,edg+m+1,cmp);
for(re i=1;i<=m;i++){
if(edg[i].typ==1)continue;
//cout<<edg[i].val<<" "<<edg[i].fr<<" "<<edg[i].to<<endl;
ans[i]=0;change(edg[i].fr,edg[i].to,edg[i].val-1,i);
}
for(re i=1;i<=m;i++){
//cout<<edg[i].typ<<" ";
if(find(edg[i].fr)!=rt||find(edg[i].to)!=rt)ans[i]=0,edg[i].typ=0;
if(edg[i].typ==1){
//cout<<idf[i]<<endl;
ans[i]=xds.query(1,1,cnt,dfn[idf[i]]);
}
printf("%d ",ans[i]);
}
}