洛谷3613睡觉困难综合征(LCT维护链信息(前后缀)+贪心)
這個題目還是很好啊QWQ很有紀念意義
首先,如果在序列上且是單次詢問的話,就是一個非常裸的貪心了QWQ這也是NOI當時原題的問題和數(shù)據(jù)范圍
我們考慮上樹的話,應(yīng)該怎么做?
我的想法是,對于每一位建一個LCT來做,然后對一個點x維護,當前是0到鏈頂?shù)慕Y(jié)果,當前是1到頂?shù)慕Y(jié)果,當前是0到底的結(jié)果,當前是1到底的結(jié)果(之所以維護后兩個是因為\(reverse\)操作的存在)
這樣的話,對于每次詢問,我就可以直接\(split\),然后貪心的詢問。
不過很可惜,這個算法的復雜度是\(O(qklogn)\)的
并不能跑過(雖然沒試過硬卡常行不行)
那么應(yīng)該怎么做呢QWQ 這時候,我選擇了看題解。
首先,經(jīng)過仔細思考,我們會發(fā)現(xiàn),QWQ對于題目來說,其實并不需要按位來做,可以直接維護\(f_0,f_1,g_0,g_1\)四個值,分別表示所有位0到鏈頂?shù)慕Y(jié)果,所有二進制位全是1的數(shù)到頂?shù)慕Y(jié)果,所有位全是0到底的結(jié)果,所有位全是1到底的結(jié)果
那么這個東西應(yīng)該怎么轉(zhuǎn)移呢?
這里合并的大概思想是 全0走到中間節(jié)點之后,是0的那幾位,相當于后面的全0走的,是1的那幾位,相當于是后面全1走的 ,全1的也是同理
而之所以這么寫,是因為運用了與的美妙的性質(zhì),只有兩邊都是1,最終結(jié)果才是1。
所以,假設(shè)對于當前是全零0來說,考慮前一半,是計算0的那幾位,要是采用了&與這個運算,如果最后走完,\(f_0\)的某一位是0,那么最一開始無論是什么,最終&完都是0,也就不會存在一開始是1的那些位影響答案。
要是這一位是1,我們要滿足與完 是1的話,同時去除一開始是1的位的貢獻,我們就需要先取反,然后再&
其他的其實也是同理
Node merge(Node a,Node b) //我們默認a在前,b在后 {Node c;c.f0=(~a.f0 & b.f0) + (a.f0 & b.f1); //這里合并的大概思想是 全0走到中間節(jié)點之后,是0的那幾位,相當于后面的全0走的,是1的那幾位,相當于是后面全1走的 c.f1=(~a.f1 & b.f0) + (a.f1 & b.f1); //而之所以這么寫,是因為運用了與的美妙的性質(zhì),只有兩邊都是1,最終結(jié)果才是1。所以,假設(shè)對于當前是0來說,只有后面全零弄出來是1,我們當前的的答案才是1,那么為了出來1,我們就需要先取反,然后再& return c; }void update(unsigned long long x) {zheng[x]=fan[x]=val[x];if (ch[x][0]){zheng[x]=merge(zheng[x],zheng[ch[x][0]]);fan[x]=merge(fan[ch[x][0]],fan[x]); }if (ch[x][1]){zheng[x]=merge(zheng[ch[x][1]],zheng[x]);fan[x]=merge(fan[x],fan[ch[x][1]]); } }其他的LCT操作的話,也就和別的沒什么區(qū)別了。
這里再講一下底下那個貪心的過程
unsigned long long ans=ling;split(x,y);//cout<<1<<endl;for (long long i=k-1;i>=0;i--){if (fan[y].f0&power[i]) {ans=ans+power[i];continue;}if (fan[y].f1 & power[i]){if (power[i]>z) continue;ans=ans+power[i];z-=power[i];}//cout<<i<<endl;}cout<<ans<<"\n";從高位向低位考慮,如果當前位填0,最終結(jié)果是1的話,那么就填0。
如果當前位填1,最終結(jié)果才能是1。我們就需要比較一下剩余的值是否比這個位填1的數(shù)大,大的話才能填
以此類推
QWQ
記得開\(unsigned\ long\ long\)
上代碼
// luogu-judger-enable-o2 #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #include<map> #include<set> #define mk makr_pair #define ll long long #define lc ch[x][0] #define rc ch[x][1]using namespace std;inline unsigned long long read() {unsigned long long x=0,f=1;char ch=getchar();while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f; }const int maxn = 1e6+1e2;struct Node{unsigned long long f0,f1; }; unsigned long long ch[maxn][3]; unsigned long long rev[maxn],fa[maxn]; Node zheng[maxn],fan[maxn]; unsigned long long n,m; Node val[maxn]; unsigned long long ling =0;unsigned long long son(unsigned long long x) {if (ch[fa[x]][0]==x) return 0;else return 1; }bool notroot(unsigned long long x) {return ch[fa[x]][0]==x || ch[fa[x]][1]==x; }void reverse(unsigned long long x) {swap(ch[x][0],ch[x][1]);swap(zheng[x].f0,fan[x].f0);swap(zheng[x].f1,fan[x].f1);rev[x]^=1; }Node merge(Node a,Node b) //我們默認a在前,b在后 {Node c;c.f0=(~a.f0 & b.f0) + (a.f0 & b.f1); //這里合并的大概思想是 全0走到中間節(jié)點之后,是0的那幾位,相當于后面的全0走的,是1的那幾位,相當于是后面全1走的 c.f1=(~a.f1 & b.f0) + (a.f1 & b.f1); //而之所以這么寫,是因為運用了與的美妙的性質(zhì),只有兩邊都是1,最終結(jié)果才是1。所以,假設(shè)對于當前是0來說,只有后面全零弄出來是1,我們當前的的答案才是1,那么為了出來1,我們就需要先取反,然后再& return c; }void update(unsigned long long x) {zheng[x]=fan[x]=val[x];if (ch[x][0]){zheng[x]=merge(zheng[x],zheng[ch[x][0]]);fan[x]=merge(fan[ch[x][0]],fan[x]); }if (ch[x][1]){zheng[x]=merge(zheng[ch[x][1]],zheng[x]);fan[x]=merge(fan[x],fan[ch[x][1]]); } }void pushdown(unsigned long long x) {if (rev[x]){if (ch[x][1]) reverse(ch[x][1]);if (ch[x][0]) reverse(ch[x][0]);rev[x]=0;} }void rotate(unsigned long long x) {unsigned long long y=fa[x],z=fa[y];unsigned long long b=son(x),c=son(y);if (notroot(y)) ch[z][c]=x;fa[x]=z;ch[y][b]=ch[x][!b];fa[ch[x][!b]]=y;ch[x][!b]=y;fa[y]=x;update(y);update(x); }unsigned long long st[maxn];void splay(unsigned long long x) {unsigned long long y=x,cnt=0;st[++cnt]=y;while (notroot(y)) y=fa[y],st[++cnt]=y;while (cnt) pushdown(st[cnt--]);while (notroot(x)){unsigned long long y=fa[x],z=fa[y];unsigned long long b=son(x),c=son(y);if (notroot(y)){if (b==c) rotate(y);else rotate(x); }rotate(x);} update(x); }void access(unsigned long long x) {for (unsigned long long y=0;x;y=x,x=fa[x]){splay(x);ch[x][1]=y;update(x);} }void makeroot(unsigned long long x) {access(x);splay(x);reverse(x); }unsigned long long findroot(unsigned long long x) {access(x);splay(x);while (ch[x][0]){pushdown(x);x=ch[x][0];} return x; }void split(unsigned long long x,unsigned long long y) {makeroot(x);access(y);splay(y); }void link(unsigned long long x,unsigned long long y) {makeroot(x);if(findroot(y)!=x) fa[x]=y; }unsigned long long power[maxn];unsigned long long ymh = 2; unsigned long long k;signed main() {n=read(),m=read(),k=read();power[0]=1;for (unsigned long long i=1;i<64;i++) power[i]=power[i-1]*ymh;for (unsigned long long i=1;i<=n;i++){unsigned long long y=read(),x=read();if (y==1){val[i]=(Node){ling,x};}if (y==2){val[i]=(Node){x,~ling};}if (y==3){val[i]=(Node){x,~x};} }for (unsigned long long i=1;i<n;i++){unsigned long long x=read(),y=read();link(x,y);} //cout<<1<<endl;for (unsigned long long i=1;i<=m;i++){unsigned long long opt=read(),x=read(),y=read(),z=read();if (opt==2){splay(x);if (y==1) val[x]=(Node){ling,z};if (y==2) val[x]=(Node){z,~ling};if (y==3) val[x]=(Node){z,~z}; }if (opt==1){unsigned long long ans=ling;split(x,y);//cout<<1<<endl;for (long long i=k-1;i>=0;i--){if (fan[y].f0&power[i]) {ans=ans+power[i];continue;}if (fan[y].f1 & power[i]){if (power[i]>z) continue;ans=ans+power[i];z-=power[i];}//cout<<i<<endl;}cout<<ans<<"\n";}}return 0; }轉(zhuǎn)載于:https://www.cnblogs.com/yimmortal/p/10161461.html
總結(jié)
以上是生活随笔為你收集整理的洛谷3613睡觉困难综合征(LCT维护链信息(前后缀)+贪心)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: uoj22 外星人(dp)
- 下一篇: C# 8.0 抢先看-- Async S