千載流年一如夢,月落山河一世傾。html
從思路上來說是比較成功的,從分數上就比較使人失望了。c++
考場上是想到了前兩個題的正解思路,其實最後一個題是半個原題,只惋惜是我看不懂題。。。數組
這波呀,這波又是 語文素養限制OI水平
。。優化
改題的時候連官方題解都沒看一眼就碼過了,感受不錯。spa
總感受出題人的題目名字有點。。。(T2的wrxcsd是啥意思????)3d
作法有不少,好比什麼:最小生成樹,魔改拓撲排序,等等。code
個人作法是 Dij 最短路,看題目第一眼就是某個點到全部點路徑上困難值最大值的最小值。htm
跑一個 Dij 以後直接對於每個距離離散化一下,進而求出當每種最大的能夠克服的困難度能夠到達的種類數。blog
觀察到問題是一個區間的值,線段樹就算了,畢竟咱們不須要修改。排序
考慮前綴和,查找兩個端點在離散化數組裏的值,對於整個的部分直接前綴和處理。
對於剩餘的部分用剩餘部分的區間長度乘上貢獻便可。
這種打法對於 \(l=r\) 彷佛是不可作的,所以直接特判就好了(逃。
惋惜我考場上一時糊塗,把離線和在線分開處理了,而後離線錯了,還沒特判,\(100pts\rightarrow 55pts\)
原本覺得切了的,我裂開。。。
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define f() cout<<"Pass"<<endl using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch>'9'||ch<'0') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=2e6+10,M=6e3+10; int n,m,ans,fro,T,opt,mod,dis[N],all[N],qzh[N],pre[N]; int tot,head[N],ver[N<<1],edge[N<<1],nxt[N<<1]; int cnt,lsh[N<<2]; bool vis[N]; priority_queue<pair<int,int> > que; struct Node { int dis,col; }s[N]; struct Ques { int l,r; }q[N]; bool comp(Node x,Node y) { return x.dis<y.dis; } void add_edge(int x,int y,int val) { ver[++tot]=y; edge[tot]=val; nxt[tot]=head[x]; head[x]=tot; } void Dij() { que.push(make_pair(0,fro)); memset(dis,0x3f,sizeof(dis)); dis[fro]=0; while(!que.empty()) { int x=que.top().second; que.pop(); if(vis[x]) continue; vis[x]=true; for(int i=head[x];i;i=nxt[i]) { int to=ver[i],val=edge[i]; if(dis[to]>max(dis[x],val)) { dis[to]=max(dis[x],val); que.push(make_pair(-dis[to],to)); } } } } signed main() { n=read(); m=read(); T=read(); fro=read(); opt=read(); if(opt) mod=read(); for(int i=1;i<=n;i++) s[i].col=read(); for(int i=1,x,y,val;i<=m;i++) { x=read(); y=read(); val=read(); add_edge(x,y,val); add_edge(y,x,val); } for(int i=1;i<=T;i++) { q[i].l=read(); q[i].r=read(); } Dij(); for(int i=1;i<=n;i++) lsh[++cnt]=s[i].dis=dis[i]; sort(lsh+1,lsh+cnt+1); cnt=unique(lsh+1,lsh+cnt+1)-lsh-1; for(int i=1;i<=n;i++) s[i].dis=lower_bound(lsh+1,lsh+cnt+1,s[i].dis)-lsh; sort(s+1,s+n+1,comp); for(int i=1;i<=n;i++) { all[s[i].col]++; int temp=0; if(all[s[i].col]==1) temp=1; qzh[s[i].dis]=qzh[s[i-1].dis]+temp; } for(int i=1;i<=cnt;i++) pre[i]=pre[i-1]+qzh[i-1]*(lsh[i]-lsh[i-1]-1)+qzh[i]; for(int i=1,li,ri;i<=T;i++) { li=q[i].l; ri=q[i].r; if(opt) { li=(li^ans)%mod+1; ri=(ri^ans)%mod+1; if(li>ri) swap(li,ri); } if(li!=ri) { int ls=upper_bound(lsh+1,lsh+cnt+1,li)-lsh; int rs=upper_bound(lsh+1,lsh+cnt+1,ri)-lsh; ans=pre[rs-1]-pre[ls]+qzh[ls]; ans+=(lsh[ls]-li)*qzh[ls-1]+(ri-lsh[rs-1])*qzh[rs-1]; } else { if(li<lsh[cnt]) { int temp=upper_bound(lsh+1,lsh+cnt+1,li)-lsh-1; if(temp<=0) ans=0; else ans=qzh[temp]; } else ans=qzh[cnt]; } printf("%lld\n",ans); } return 0; }
反正我這個法是個暴力,可是能切題就行。
題庫數據是能夠直接打過去,可是 yspm 和 zero4338 造了兩組數據,成功 Hack 掉了我。。
我又看了一下數據,誒,又是鏈,我直接來一套 「組合拳」,其實就是對於不一樣的採用不一樣的打法。
這個能夠切掉除了Hack數據以外的點,可是優化以前容易被各類鏈給卡掉(優化思想來自 戰神 )
首先,對於每個開始不喜歡的點(之後簡稱爲插入)其實須要的只是他插入的時間和位置。
所以咱們開一個數組記錄下這兩個值。
而後在每次查詢的時候暴力枚舉數組裏每個點與如今查詢的點的距離與時間差的關係。
距離能夠用 RMQ 或者 樹鏈剖分 來搞,理論上來說 RMQ 是更快的。
可是實際上,由於 RMQ 初始化時間比較長,因此仍是 樹鏈剖分 可能更快一些。
這樣的預估分數是 \(40pts\sim 60pts\) 可是出乎意料地搞到了 90pts!!。
優化插入部分,咱們發現其實有些節點能夠傳到的點是能夠在別的節點也傳到的。
那麼咱們就沒有必要把它給插進去,所以,在每一次插入以前進行一次相似於查詢的操做就行了。
卡掉上面的作法只須要讓咱們的存儲數組裏的元素足夠多,而且每一次查詢都查詢最遠的兩個端點就行了。
所以,WindZR 想出了這樣一種作法,適用於全部的相似於鏈(一個深度十分大的主幹上有一些小的分支)
其實就是優化後的擴散。。。
發現每一個點有用的擴散只有一次,所以咱們記錄上一次數組裏擴散到的位置。
而後再此基礎上接着擴散一個表示每個是否被擴散到就行了。
清空操做直接清空數組就行了。
這兩種作法(不要在乎代碼裏的\(\dfrac{5}{8}\),原本想卡個邊界的可是好像不卡也行)的時間複雜度都有一點玄學(至少我是這麼想的),我最快卡到了 500ms+(數據增強後)
固然也有強者卡進了500ms之內,但畢竟是個人作法大衆普及了。。。
也不知道爲啥機房裏有很多人都在 Hack 我。。。。。
好像這兩種作法的結合也能夠被卡掉,能夠搞一個相似於神經元的圖(其實就是鏈+菊花圖)。
或者一種長菊花圖(就是讓多條鏈的端點鏈接到一個節點上)(可fengwu說卡不掉,有異議的請線下解決)
可是這種數據有點難造,因此歡迎各位前來 Hack 。
另外,yspm的線段樹作法(\(code\))或者其餘的點分治還有 火羽白日生 的虛樹(\(code\))好像也能夠。。。
#include<bits/stdc++.h> using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch>'9'||ch<'0') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=1e5+10; int n,m,las,top; int tot,head[N],nxt[N<<1],ver[N<<1]; int tim,dep[N],topp[N],siz[N],dfn[N],son[N],fa[N]; int cnt,st[N],mxdep; bool flag,vis[N]; struct Node { int pos,tim; }sta[N]; void add_edge(int x,int y) { ver[++tot]=y; nxt[tot]=head[x]; head[x]=tot; } void dfs1(int x) { mxdep=max(mxdep,dep[x]); siz[x]=1; for(int i=head[x];i;i=nxt[i]) { int to=ver[i]; if(siz[to]) continue; fa[to]=x; dep[to]=dep[x]+1; dfs1(to); siz[x]+=siz[to]; if(siz[to]>siz[son[x]]) son[x]=to; } } void dfs2(int x,int tp) { dfn[x]=++tim; topp[x]=tp; if(son[x]) dfs2(son[x],tp); for(int i=head[x];i;i=nxt[i]) if(!dfn[ver[i]]) dfs2(ver[i],ver[i]); } int LCA(int x,int y) { if(!x||!y) return 0; while(topp[x]^topp[y]) { if(dep[topp[x]]<dep[topp[y]]) swap(x,y); x=fa[topp[x]]; } if(dep[x]>dep[y]) swap(x,y); return x; } int dist(int x,int y) { return dep[x]+dep[y]-2*dep[LCA(x,y)]; } void Special_Judge() { for(int i=1,opt,x;i<=m;i++) { opt=read(); x=read(); int pre=cnt; for(int k=las+1;k<=pre;k++) for(int j=head[st[k]];j;j=nxt[j]) { if(!vis[ver[j]]) st[++cnt]=ver[j]; vis[ver[j]]=true; } las=pre; if(opt==2) { memset(vis,false,sizeof(vis)); cnt=las=0; } else if(opt==1) { if(!vis[x]) st[++cnt]=x,vis[x]=true; } else { if(vis[x]) printf("wrxcsd\n"); else printf("orzFsYo\n"); } } exit(0); } signed main() { n=read(); m=read(); for(int i=1,x,y;i<n;i++) { x=read(); y=read(); add_edge(x,y); add_edge(y,x); if(x!=y+1&&y!=x+1) flag=true; } dfs1(1); dfs2(1,1); if(!flag||mxdep>=n*5/8) Special_Judge(); for(int i=1,opt,x;i<=m;i++) { opt=read(); x=read(); if(opt==2) top=0; else if(opt==1) { bool jud=true; for(int j=1;j<=top;j++) if(dist(sta[j].pos,x)<=i-sta[j].tim) { jud=false; break; } if(jud) sta[++top]=(Node){x,i}; } else { bool jud=false; for(int j=1;j<=top;j++) if(dist(sta[j].pos,x)<=i-sta[j].tim) { jud=true; break; } if(jud) printf("wrxcsd\n"); else printf("orzFsYo\n"); } } return 0; }
其實就是極長上升序列。
和以前作的God Kowns
幾乎是一個題,仍是李超線段樹或者線段樹維護單調棧。。
今天剛打了疫苗有點困,可能口胡說不清,因此不懂的請移步 God Kowns
#include<bits/stdc++.h> #define int long long #define ls x<<1 #define rs x<<1|1 #define ull unsigned long long #define f() cout<<"Pass"<<endl using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch>'9'||ch<'0') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=2e5+10,mod=998244353; int n,mx,s[N],q[N<<2]; struct Segment_Tree { int mx,sum; }tre[N<<2]; int work(int x,int l,int r,int num) { if(l==r) { if(tre[x].mx>num) return tre[x].sum%mod; return 0; } int mid=(l+r)>>1; if(tre[rs].mx>num) return (q[ls]+work(rs,mid+1,r,num)%mod)%mod; return work(ls,l,mid,num)%mod; } int query(int x,int l,int r,int L,int R) { if(L<=l&&r<=R) { int temp=work(x,l,r,mx); mx=max(mx,tre[x].mx); return temp%mod; } int mid=(l+r)>>1,sum=0; if(R>mid) sum=query(rs,mid+1,r,L,R)%mod; if(L<=mid) sum=(sum+query(ls,l,mid,L,R)%mod)%mod; return sum%mod; } void update(int x,int l,int r,int pos,int num1,int num2) { if(l==r) { tre[x].sum=q[x]=num1; tre[x].mx=num2; return ; } int mid=(l+r)>>1; if(pos<=mid) update(ls,l,mid,pos,num1,num2); else update(rs,mid+1,r,pos,num1,num2); tre[x].mx=max(tre[ls].mx,tre[rs].mx); q[ls]=work(ls,l,mid,tre[rs].mx)%mod; tre[x].sum=(q[ls]+tre[rs].sum)%mod; } signed main() { n=read(); for(int i=1;i<=n;i++) s[i]=read(); for(int i=1;i<=n;i++) { mx=-1; int val=query(1,0,n+1,0,s[i]-1)%mod; if(!val) val=1; update(1,0,n+1,s[i],val,i); } mx=-1; printf("%lld",query(1,0,n+1,0,n)%mod); return 0; }