[NOIP2018模擬賽10.25]瞎搞報告
閑扯
最近有點頹,都修到好晚,早上起來和吔shi一樣難受
忍著困意把題面看完,發現啥也不會,又是一場寫暴力的模擬賽
T1發現似乎可以DP,順手碼了個
T2像個最小瓶頸路板子,但是只做過N^2算法的...
T3我是真的傻,估計全場就我一人以為只能往前跳於是寫了個DP
結果30+35+0
然後發現T1爆了,後面都輸出負數,全部用long long 後交了發,居然95?!wtf
後面發現最naiive的貪心都有90,這數據比聯賽還水啊,後面發現只有一個點的一次詢問答案不一樣,打個表就A了
T1 colour
gu
T2 graph
一看就發現這種邊就是最小瓶頸路(邊),根據最小生成樹也是最小瓶頸生成樹的性質,我們可以在最小生成樹上DFS求到所有點對的最小瓶頸路.
但怎麽找符合條件的點對?對於L=0的子任務,我們可以在Kruskal過程中每新加入一條邊就計算兩個聯通塊大小的乘積,由於我們的邊是從小到大排序的,這條新加入的邊一定是這兩個聯通塊點對之間的最小瓶頸路
正解使用了Kruskal重構樹,不了解的先去學習一下(我也是做這題才學的)
容易發現,兩個原樹上的點在重構樹上的LCA的點權就是兩點間的最小瓶頸路長度,這樣只需要求一個LCA的時間復雜度就可以得到兩點之間的最小瓶頸路
然後運用啟發式合並的思路,我們可以枚舉原樹上的每一條邊計算它的貢獻,然後進入重構樹上對應點相鄰的兩棵子樹中較小的那一棵枚舉點,這樣就能得到了兩個約束條件:一個是DFS序的約束(因為另一個點必須在另一棵較大的子樹中),一個是顏色範圍的約束
又轉化成並不喜聞樂見的二維數點問題,離線+樹狀數組+二維前綴和即可
然後發現WA了,xxzh大佬說要註意l=0的情況,改後又RE了,交了幾發終於A了...不過跑得好慢
其實這題一開始想線段樹合並的,但是沒有想到啟發式合並,晚上有大佬提供線段樹合並的思路就是每個聯通塊維護權值線段樹,Kruskal連一條邊的時候,進入較小的塊枚舉然後在另一棵樹上線段樹查詢
這樣的話也是兩個log
/* code by RyeCatcher */ inline char gc(){ static char buf[SIZE],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,SIZE,stdin),p1==p2)?EOF:*p1++; } template <class T>inline void read(T &x){ x=0;int ne=0;char c; while((c=gc())>‘9‘||c<‘0‘)ne=c==‘-‘;x=c-48; while((c=gc())>=‘0‘&&c<=‘9‘)x=(x<<3)+(x<<1)+c-48;x=ne?-x:x;return ; } const int maxn=600005; const int inf=0x7fffffff-10; int n,m,l,c[maxn],mx_c=0; struct Nico{ int x,y,dis; bool operator <(const Nico &rhs)const{ return dis<rhs.dis; } }nico[maxn]; int pa[maxn]; int get(int x){return (pa[x]==x)?(pa[x]):(pa[x]=get(pa[x]));} int cnt=0,w[maxn<<1],fa[maxn<<1],ch[maxn<<1][2],size[maxn<<1]; int dfn[maxn<<1],tot=0,ed[maxn<<1]; ll sum[maxn<<3],qry[maxn]; inline void add(int x,int d){for(;x<=2*n-1;x+=x&(-x))sum[x]+=d;} inline ll query(int x){ll tmp=0;for(;x>=1;x-=x&(-x))tmp+=sum[x];return tmp;} struct QAQ{ int x,y,d,id; bool operator <(const QAQ & rhs)const{ return (x==rhs.x)?id<rhs.id:x<rhs.x; } }qwq[maxn*25];int num=0; void pre_dfs(int now){ int v; dfn[now]=++tot,size[now]=1; //printf("--%d %d--\n",now,fa[now]); if(ch[now][0])pre_dfs(ch[now][0]); if(ch[now][1])pre_dfs(ch[now][1]); size[now]+=(size[ch[now][0]]+size[ch[now][1]]); ed[now]=tot; if(now>=1&&now<=n){ qwq[++num]=(QAQ){c[now],dfn[now],1,0}; //printf("%d %d\n",dfn[now],c[now]); } return ; } int L,R,LL,RR; void dfs(int now,int id){ int v; if(ch[now][0])dfs(ch[now][0],id); if(ch[now][1])dfs(ch[now][1],id); //LL=max(0,c[now]-l),RR=min(c[now]+l,mx_c); if(now>n||now<1)return ; LL=c[now]-l,RR=c[now]+l;if (!l) RR++; qwq[++num]=(QAQ){-inf-1,L-1,1,id}; qwq[++num]=(QAQ){-inf-1,R,-1,id}; qwq[++num]=(QAQ){LL,L-1,-1,id}; qwq[++num]=(QAQ){LL,R,1,id}; qwq[++num]=(QAQ){RR-1,L-1,1,id}; qwq[++num]=(QAQ){RR-1,R,-1,id}; qwq[++num]=(QAQ){inf,L-1,-1,id}; qwq[++num]=(QAQ){inf,R,1,id}; //printf("%d %d %d %d %d %d\n",now,L,R,LL,RR,id); return ; } int main(){ //freopen("graph19.in","r",stdin); FO(graph); int x,y,z; read(n),read(m),read(l); for(ri i=1;i<=n;i++)pa[i]=i,read(c[i]),mx_c=max(mx_c,c[i]); for(ri i=n+1;i<=n*2+2;i++)pa[i]=i; for(ri i=1;i<=m;i++){ read(x),read(y),read(z); nico[i]=(Nico){x,y,z}; } std::sort(nico+1,nico+1+m); cnt=n; for(ri i=1;i<=m;i++){ x=nico[i].x,y=nico[i].y; x=get(x),y=get(y); if(x==y)continue; pa[x]=++cnt,pa[y]=cnt; fa[x]=cnt,fa[y]=cnt,ch[cnt][0]=x,ch[cnt][1]=y; w[cnt]=nico[i].dis; //printf("%d %d()()()\n",cnt,w[cnt]); if(cnt==2*n-1)break; } fa[cnt]=0; pre_dfs(cnt); //for(ri i=1;i<=cnt;i++)printf("--%d %d %d %d %d %d--\n",i,fa[i],ch[i][0],ch[i][1],dfn[i],ed[i]); for(ri i=n+1;i<=cnt;i++){ x=ch[i][0],y=ch[i][1]; if(size[x]>size[y])std::swap(x,y); L=dfn[y],R=ed[y]; //printf("**%d %d %d %d\n",i,L,R,w[i]); dfs(x,i); } std::sort(qwq+1,qwq+1+num); for(ri i=1;i<=num;i++){ if(qwq[i].id==0){ //printf("%d %d\n",qwq[i].y,qwq[i].d); add(qwq[i].y,1); } else{ //printf("%d %d %lld %d %d\n",qwq[i].id,qwq[i].d,query(qwq[i].y),qwq[i].x,qwq[i].y); qry[qwq[i].id]+=qwq[i].d*query(qwq[i].y); } }//return 0; ll ans=0; //for(ri i=n+1;i<=cnt;i++)printf("&&&%d %d\n",i,w[i]); for(ri i=n+1;i<=cnt;i++){ //printf("%d %d %d\n",i,qry[i],w[i]); ans+=qry[i]*w[i]; } printf("%lld\n",ans); return 0; }
[NOIP2018模擬賽10.25]瞎搞報告