UOJ 2021 NOI Day2 部分題解
阿新 • • 發佈:2021-07-20
獲獎名單
Solution
不難看出,若我們單個 \(x\) 連 \((0,x),(x,0)\),兩個連 \((x,y),(y,x)\) ,除去中間過對稱軸的一個兩個組,就是找很多個歐拉回路。
直接來就好了。
Code
#include <bits/stdc++.h> using namespace std; #define Int register int #define MAXN 500005 template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;} template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);} template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');} #define pii pair<int,int> #define se second #define fi first int n,m; bool vis[MAXN]; vector <pii> g[MAXN]; int cnt,seq[MAXN]; void dfs (int u){ while (!g[u].empty()){ pii it = g[u].back(); g[u].pop_back(); if (vis[abs(it.se)]) continue; vis[abs (it.se)] = 1,dfs (abs (it.fi)),seq[++ cnt] = it.se; } } signed main(){ read (n,m); for (Int i = 1;i <= n;++ i){ int opt,x,y;read (opt,x); if (opt == 1) g[0].push_back ({x,-i}),g[x].push_back ({0,i}); else read (y),g[x].push_back ({y,i}),g[y].push_back ({x,-i}); } dfs (0); vector <int> ansl,ansr; for (Int i = 1;i <= cnt;i += 2) ansl.push_back (seq[i]); for (Int i = 2;i <= cnt;i += 2) ansr.push_back (-seq[i]); int mid = 0; for (Int i = 1;i <= m;++ i){ sort (g[i].begin(),g[i].end()); for (Int j = 0;j < g[i].size();j += 2){ if (g[i][j].fi == i && g[i][j].se == -g[i][j + 1].se){ mid = abs(g[i][j].se); continue; } if (g[i][j].fi > i || (g[i][j].fi == i && g[i][j].se < 0)) ansl.push_back (g[i][j].se), ansr.push_back (-g[i][j + 1].se); } } for (Int i = ansl.size() - 1;~i;-- i) write (abs(ansl[i])),putchar (' '); if (mid) write (mid),putchar (' '); for (Int i = 0;i < ansr.size();++ i) write (abs(ansr[i])),putchar (' '); putchar ('\n'); for (Int i = ansl.size() - 1;~i;-- i) write (ansl[i] < 0),putchar (' '); if (mid) write (0),putchar (' '); for (Int i = 0;i < ansr.size();++ i) write (ansr[i] < 0),putchar (' '); putchar ('\n'); return 0; }
詭異操作
Solution
考慮使用線段樹維護,對於一個線段樹上的區間,我們可以維護 \(s_c\) 表示二進位制位出現次數二進位制第 \(c\) 位為 \(1\) 的和。你發現這個東西可以進行合併,並且取並操作可以直接對於每一個 \(s_c\) 並。
然後對於下取整操作可以直接暴力重構。
複雜度是個米奇妙妙複雜度,據說是 \(\Theta(128n+q\log^2n)\)。
Code
#include <bits/stdc++.h> using namespace std; #define Int register int #define MAXN 300005 template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;} template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);} template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');} typedef __uint128_t u128; inline u128 read() { static char buf[100]; scanf("%s", buf); // std::cin >> buf; u128 res = 0; for(int i = 0;buf[i];++i) { res = res << 4 | (buf[i] <= '9' ? buf[i] - '0' : buf[i] - 'a' + 10); } return res; } inline void output(u128 res) { if(res >= 16) output(res / 16); putchar(res % 16 >= 10 ? 'a' + res % 16 - 10 : '0' + res % 16); //std::cout.put(res % 16 >= 10 ? 'a' + res % 16 - 10 : '0' + res % 16); } #define ll u128 ll a[MAXN]; int n,q,len[MAXN << 2]; ll res = ~ll(0); struct Segment{ ll val[MAXN << 2][20],any[MAXN << 2],laz[MAXN << 2]; void pushup (int x){ memset (val[x],0,sizeof (u128) * len[x]); int ls = x << 1,rs = x << 1 | 1; val[x][0] = val[ls][0] ^ val[rs][0];ll car = val[ls][0] & val[rs][0]; for (Int i = 1;i < len[x];++ i){ ll w = val[ls][i] ^ val[rs][i]; val[x][i] = w ^ car,car = (car & w) | (val[ls][i] & val[rs][i]); } val[x][len[x]] = car,any[x] = any[x << 1] | any[x << 1 | 1]; } ll getv (int x){ ll ans = 0; for (Int i = 0;i <= len[x];++ i) ans += val[x][i] << i; return ans; } void pushadd (int x,ll V){ for (Int i = 0;i <= len[x];++ i) val[x][i] &= V; laz[x] &= V,any[x] &= V; } void pushdown (int x){pushadd (x << 1,laz[x]),pushadd (x << 1 | 1,laz[x]),laz[x] = res;} void build (int x,int l,int r){ laz[x] = res,len[x] = 1; while ((1 << len[x]) <= (r - l + 1)) ++ len[x]; if (l == r) return val[x][0] = any[x] = a[l],void (); int mid = l + r >> 1; build (x << 1,l,mid),build (x << 1 | 1,mid + 1,r); pushup (x); } void modify1 (int x,int l,int r,int ql,int qr,ll V){ if (l >= ql && r <= qr) return pushadd (x,V),void (); int mid = l + r >> 1;pushdown (x); if (ql <= mid) modify1 (x << 1,l,mid,ql,qr,V); if (qr > mid) modify1 (x << 1 | 1,mid + 1,r,ql,qr,V); pushup (x); } void div (int x,int l,int r,ll V){ if (!any[x]) return ; if (l == r) return any[x] = (val[x][0] /= V),void (); int mid = l + r >> 1;pushdown (x); div (x << 1,l,mid,V),div (x << 1 | 1,mid + 1,r,V); pushup (x); } ll query (int x,int l,int r,int ql,int qr){ if (l >= ql && r <= qr) return getv (x); int mid = l + r >> 1;pushdown (x);ll res = 0; if (ql <= mid) res += query (x << 1,l,mid,ql,qr); if (qr > mid) res += query (x << 1 | 1,mid + 1,r,ql,qr); return res; } void modify2 (int x,int l,int r,int ql,int qr,ll V){ if (l >= ql && r <= qr) return div (x,l,r,V); int mid = l + r >> 1;pushdown (x); if (ql <= mid) modify2 (x << 1,l,mid,ql,qr,V); if (qr > mid) modify2 (x << 1 | 1,mid + 1,r,ql,qr,V); pushup (x); } }T; signed main(){ read (n,q); for (Int i = 1;i <= n;++ i) a[i] = read (); T.build (1,1,n); while (q --> 0){ int opt,qL,qR;ll V; read (opt,qL,qR); if (opt <= 2){ V = read (); if (opt == 1 && V > 1) T.modify2 (1,1,n,qL,qR,V); else if (opt == 2) T.modify1 (1,1,n,qL,qR,V); } else output (T.query (1,1,n,qL,qR)),putchar ('\n'); } return 0; }