題意:
給出一個n個點,m條邊的dag,每個點有點權
1 u x:將所有u能到達的點的權值設爲x。
2 u x:將所有u能到達的點的權值對x取min。
3 u:查詢u的點權。
又用bitset強上了一道題好開心啊~(然而這題標算似乎就是bitset?)
如果暴力的話,可以用bitset,但是空間爆炸;所以我們先解決一下空間問題。
如果考慮對操作分塊的話,我們可以只用處理所有節點能否由塊內的操作節點抵達,這樣首先就可以解決bitset的空間問題!
然後一開始我是這樣想的:
如果我們可以
但是我們仔細考慮一下,對u的詢問的答案是能到達它的最後一個1操作的x和之間所有的2操作的x最小值。這樣的話其實我們沒必要處理出每個塊的最終狀態。我們只需要找到這最後一個1在哪個塊裏,這是可以對每個塊用一個
時間複雜度
看了題解,發現它是這麼解決空間問題的:把所有的節點分成3份,對於暴力部分計算3次——這時我才突然明白,bitset可以開到
發現不知不覺似乎已經用這種類似對時間分塊的方法a了很多題了。。而且做法主要是有兩種,一種是處理出每個塊的最終狀態,這種適用於那種很多操作一起處理比較方便的,一種是查詢的是一個點,那麼我們可以分塊找出這個點。
似乎如果允許離線,而且本來的點與點之間的關係比較奇怪,對時間分塊還是一個不錯的搞法呢~
代碼:
#include<stdio.h>
#include<iostream>
using namespace std;
#include<algorithm>
#include<cstring>
#include<bitset>
char * cp=(char *)malloc(4000000);
inline void in(int &x)
{
while(*cp<'0'||*cp>'9')++cp;
for(x=0;*cp>='0'&&*cp<='9';)x=x*10+(*cp++^'0');
}
const int N=1e5+5,M=1e5+5,Q=1e5+5;
int nxt[M],succ[M],ptr[N],etot=1;
inline void addedge(int u,int v)
{
nxt[etot]=ptr[u],ptr[u]=etot,succ[etot++]=v;
}
bool exist_deg[N];
int indeg[N];
int topo[N],ttot=1;
void topo_dfs(int node)
{
topo[ttot++]=node;
for(int i=ptr[node];i;i=nxt[i])
if(--indeg[succ[i]]==0)
topo_dfs(succ[i]);
}
struct PS
{
int opt,u,x,i;
}perform[Q];
struct QS
{
int u,i;
}que[Q];
int ans[Q];
const int B=316;
//const int B=4;
bitset<320> b[N];
bool cover[N];
int Min[N];
int main()
{
freopen("dagq.in","r",stdin);
freopen("dagq.out","w",stdout);
fread(cp,1,4000000,stdin);
int n,m,q;
in(n),in(m),in(q);
for(int u,v;m--;)
{
in(u),in(v);
addedge(u,v);
exist_deg[v]=1;
++indeg[v];
}
for(int i=n;i;--i)
if(!exist_deg[i])
topo_dfs(i);
int ptot=0,qtot=0;
for(int i=1;i<=q;++i)
{
in(perform[ptot].opt);
if(perform[ptot].opt<=2)
{
in(perform[ptot].u),in(perform[ptot].x);
perform[ptot++].i=i;
}
else
{
in(que[qtot].u);
que[qtot++].i=i;
}
}
memset(ans,127,sizeof(ans));
//printf("ptot=%d,qtot=%d\n",ptot,qtot);
for(int l=(ptot-1)-(ptot-1)%B,r=ptot-1;l>=0;r=l-1,l-=B)
{
/*printf("---[%d,%d]:[%d,%d]---\n",l,r,perform[l].i,perform[r].i);
printf("perform=");
for(int i=l;i<=r;++i)printf("(opt=%d,u=%d,x=%d,i=%d) ",perform[i].opt,perform[i].u,perform[i].x,perform[i].i);
puts("");*/
for(int i=n;i;--i)b[i].reset();
for(int i=l;i<=r;++i)b[perform[i].u][i-l]=1;
for(int i=1;i<=n;++i)
for(int j=ptr[topo[i]];j;j=nxt[j])
b[succ[j]]|=b[topo[i]];
/*for(int i=1;i<=n;++i)
{
printf("%d:",topo[i]);
for(int j=0;j<=r-l;++j)printf("%d",(int)b[topo[i]][j]);
puts("");
}*/
memset(cover,0,sizeof(cover));
for(int i=l;i<=r;++i)
if(perform[i].opt==1)
cover[perform[i].u]=1;
for(int i=1;i<=n;++i)
if(cover[topo[i]])
for(int j=ptr[topo[i]];j;j=nxt[j])
cover[succ[j]]=1;
memset(Min,127,sizeof(Min));
for(int i=l;i<=r;++i)
if(perform[i].opt==2)
Min[perform[i].u]=min(Min[perform[i].u],perform[i].x);
for(int i=1;i<=n;++i)
for(int j=ptr[topo[i]];j;j=nxt[j])
Min[succ[j]]=min(Min[succ[j]],Min[topo[i]]);
{
int i=qtot;
while(i&&que[i-1].i>perform[l].i)--i;
while(i<qtot)
{
//printf("Q:u=%d,i=%d,now=%d\n",que[i].u,que[i].i,ans[que[i].i]);
if(que[i].i<perform[r].i)
{
int j=r;
while(perform[j].i>que[i].i)--j;
for(;j>=l;--j)
if(b[que[i].u][j-l])
{
ans[que[i].i]=min(ans[que[i].i],perform[j].x);
if(perform[j].opt==1)
{
swap(que[i],que[--qtot]);
break;
}
}
i+=j<l;
}
else
if(cover[que[i].u])
{
int j=r;
for(;perform[j].opt==2||!b[que[i].u][j-l];--j)
if(perform[j].opt==2&&b[que[i].u][j-l])
ans[que[i].i]=min(ans[que[i].i],perform[j].x);
ans[que[i].i]=min(ans[que[i].i],perform[j].x);
swap(que[i],que[--qtot]);
}
else
{
ans[que[i].i]=min(ans[que[i].i],Min[que[i].u]);
++i;
}
}
}
}
while(qtot--)ans[que[qtot].i]=0;
for(int i=1;i<=q;++i)
if(ans[i]<=1e9)
printf("%d\n",ans[i]);
}
總結:
①一定要想好細節再寫代碼!
②bitset佔的內存是
③對時間分塊:對每個塊處理出最終狀態;操作對查詢的影響是一個關於時間的點/區間。