背叛 仇恨 消极 如刀子刺穿了铁心 嘲笑 嗤之以鼻 漠然后只剩下孤寂
test20
异或xor
首先 Bob 可以将 \(a\) 两两匹配,永远拿跟 Alice 匹配的另一个,现在问题变成了最小化 \(\{a_{l_i}\oplus a_{r_i}\}\)。
比较经典的从高位到低位贪心,先考虑第一位,\(0/1\) 的数量都是偶数的话答案的这一位可以是 \(0\),否则必须拿至少一对第一位不相等的,因为可以做到别的对这一位相等所以一定是特殊对贡献答案,不妨考虑异或值最小的特殊对作为答案。如果第一位可以是 \(0\),那就考虑下一位,历史前缀相同的归为一类就是跟刚刚一样的问题了,如果存在不能完美划分的就取 \(\min\) 贡献的 \(\max\) 作为答案输出即可,实现可以在 trie 上。
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pii pair<int,int>
#define mp make_pair
using namespace std;
const int N=200005, M=10000005;
int id, T, n, a[N], val[M], son[M][2], res[M], tot=1;
void insert(int x) {
int p=1;
dn(i,29,0) {
int to=(x>>i)&1;
if(!son[p][to]) son[p][to]=++tot;
p=son[p][to], ++val[p], res[p]=((x>>i)<<i);
}
}
int rmq(int v,int p) {
if(!son[p][0]&&!son[p][1]) return res[p]^v;
if(!son[p][1]) return rmq(v,son[p][0]);
if(!son[p][0]) return rmq(v,son[p][1]);
if((res[son[p][0]]^v)<(res[son[p][1]]^v)) return rmq(v,son[p][0]);
return rmq(v,son[p][1]);
}
int find(int p,int rt) {
if(!son[p][0]&&!son[p][1]) return rmq(res[p],rt);
int l=1e18, r=1e18;
if(son[p][0]) l=find(son[p][0],rt);
if(son[p][1]) r=find(son[p][1],rt);
return min(l,r);
}
pii ans(int p,int d) {
if(!p) return mp(0,1e9);
if(val[son[p][0]]%2==0) {
pii l=ans(son[p][0],d+1);
pii r=ans(son[p][1],d+1);
if(l.second<r.second||l.second==r.second&&l.first>r.first) return l;
return r;
}
int ret=find(son[p][1],son[p][0]);
return mp(ret,d);
}
void mian() {
memset(val,0,sizeof(val));
memset(son,0,sizeof(son));
memset(res,0,sizeof(res));
cin >> n, tot=1;
up(i,1,2*n) cin >> a[i], insert(a[i]);
cout << ans(1,0).first << '\n';
}
signed main() {
freopen("xor.in","r",stdin);
freopen("xor.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> id >> T;
while(T--) mian();
return 0;
}
方sq
设 \(d_i\) 表示 \(i\) 收到的 操作 \(2\) 的次数-操作 \(1\) 的次数,显然我们只关心 \(d_i\) 的历史最小值和最终值。知道历史最小值后可以暴力还原,知道最终值可以用快速幂,用扩展欧几里得定理处理指数即可。但是话又说回来了刷新底线操作 \(1\) 反正是有限势能的,不妨就暴力去做,具体而言操作 \(2\) 正常打 lazytag,操作 \(1\) 就是优先下降 lazytag 如果不够了就暴力去做,还要额外维护一个区间存不存在 \(a_i>1\) 这样。
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
using namespace std;
const int N=300005, P=998244353, phi=P-1;
int id, T, n, m, a[N], d[N<<2], tag[N<<2];
int ksm(int a,int b,int P) {
int ret=1;
for( ; b; b>>=1) {
if(b&1) ret=ret*a%P;
a=a*a%P;
}
return ret;
}
inline void tup(int p) {
tag[p]=tag[ls(p)]|tag[rs(p)];
}
inline void tdn(int p) {
if(!d[p]) return;
d[ls(p)]+=d[p], d[rs(p)]+=d[p], d[p]=0;
}
void build(int p=1,int s=1,int e=n) {
d[p]=0;
if(s==e) { tag[p]=(a[s]>1); return; }
int mid=(s+e)>>1;
build(ls(p),s,mid), build(rs(p),mid+1,e);
tup(p);
}
void add(int l,int r,int p=1,int s=1,int e=n) {
if(l<=s&&e<=r) { ++d[p]; return; }
tdn(p);
int mid=(s+e)>>1;
if(l<=mid) add(l,r,ls(p),s,mid);
if(r>mid) add(l,r,rs(p),mid+1,e);
}
void lower(int p,int s,int e) {
if(!tag[p]) return;
if(d[p]>0) { --d[p]; return; }
if(s==e) { a[s]=sqrt(a[s]), tag[p]=(a[s]>1); return; }
tdn(p);
int mid=(s+e)>>1;
lower(ls(p),s,mid), lower(rs(p),mid+1,e);
tup(p);
}
void del(int l,int r,int p=1,int s=1,int e=n) {
if(l<=s&&e<=r) { lower(p,s,e); return; }
tdn(p);
int mid=(s+e)>>1;
if(l<=mid) del(l,r,ls(p),s,mid);
if(r>mid) del(l,r,rs(p),mid+1,e);
tup(p);
}
void dfs(int p=1,int s=1,int e=n) {
if(s==e) {
int u=(d[p]<=29)?(1ll<<d[p]):(ksm(2,d[p],phi)+phi);
cout << ksm(a[s],u,P) << ' ';
return;
}
tdn(p);
int mid=(s+e)>>1;
dfs(ls(p),s,mid), dfs(rs(p),mid+1,e);
}
void mian() {
cin >> n >> m;
up(i,1,n) cin >> a[i];
build();
while(m--) {
int opt, l, r;
cin >> opt >> l >> r;
if(opt==1) del(l,r);
if(opt==2) add(l,r);
}
dfs(1), cout << '\n';
}
signed main() {
freopen("sq.in","r",stdin);
freopen("sq.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> id >> T;
while(T--) mian();
return 0;
}
括号游戏bracket
考虑一个形态的序列怎么计算答案,\(\text{(...(,,,(,,,))...(,,,)...)}\) 发现对于最外面的一对括号 \(\text{.}\) 的部分需要匹配并且独立出来惹,只要考虑长度为 \(n\) 的序列的方案书 \(f_n\) 即可求解出方案数。
\(f\) 是卡特兰数,有 \(f_{n}=\frac{1}{n+1}\binom{2n}{n}\),这个可以反射容斥算(就是将军饮马)。
然后再考虑一下维护每队存在括号的 \(\text{.}\) 数,发现关心刚加入的时候的 \(.\) 数以及过去包含它的最小(二维偏序的意思)括号对,前者可以线段树维护区间加区间最小值,后者可以线段树二分。午休的时候 yy 了一下,快速找父亲可以时光倒流然后并查集来着。
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
using namespace std;
const int N=400005, inf=1e13, P=998244353;
int id, T, n, a[N], b[N], t[N], tr[N<<2], stk[N], top, Rmq, Jury;
int res[N], pre[N], f[N], g[N], mul[N], inv[N], cnt[N<<2], tag[N<<2];
inline int C(int n,int m) {
if(m<0||n<m) return 0;
return mul[n]*inv[m]%P*inv[n-m]%P;
}
int ksm(int a,int b=P-2) {
int ret=1;
for( ; b; b>>=1) {
if(b&1) ret=ret*a%P;
a=a*a%P;
}
return ret;
}
void build(int p=1,int s=1,int e=n+1) {
tr[p]=inf;
if(s==e) return;
int mid=(s+e)>>1;
build(ls(p),s,mid), build(rs(p),mid+1,e);
}
void updata(int x,int p=1,int s=1,int e=n+1) {
if(s==e) { tr[p]=stk[x]; return; }
int mid=(s+e)>>1;
if(x<=mid) updata(x,ls(p),s,mid);
if(x>mid) updata(x,rs(p),mid+1,e);
tr[p]=min(tr[ls(p)],tr[rs(p)]);
}
int lower(int v,int p=1,int s=1,int e=n+1) {
if(s==e) return tr[p];
int mid=(s+e)>>1;
if(tr[rs(p)]<v) return lower(v,rs(p),mid+1,e);
return lower(v,ls(p),s,mid);
}
int upper(int l,int r=n+1,int p=1,int s=1,int e=n+1) {
if(tr[p]==inf) return 0;
int mid=(s+e)>>1, ret=0;
if(l<=s&&e<=r) {
if(s==e) return s;
if(tr[ls(p)]<inf) return upper(l,r,ls(p),s,mid);
return upper(l,r,rs(p),mid+1,e);
}
if(l<=mid) ret=upper(l,r,ls(p),s,mid);
if(!ret) ret=upper(l,r,rs(p),mid+1,e);
return ret;
}
inline void Tup(int p) {
if(tr[ls(p)]==tr[rs(p)]) {
tr[p]=tr[ls(p)];
cnt[p]=cnt[ls(p)]+cnt[rs(p)];
}
else if(tr[ls(p)]<tr[rs(p)]) {
tr[p]=tr[ls(p)], cnt[p]=cnt[ls(p)];
}
else tr[p]=tr[rs(p)], cnt[p]=cnt[rs(p)];
}
inline void Tdn(int p) {
if(tag[p]) {
tag[ls(p)]+=tag[p], tr[ls(p)]+=tag[p];
tag[rs(p)]+=tag[p], tr[rs(p)]+=tag[p];
tag[p]=0;
}
}
void Build(int p=1,int s=1,int e=2*n) {
tag[p]=0;
if(s==e) { tr[p]=tag[p]=0, cnt[p]=1; return; }
int mid=(s+e)>>1;
Build(ls(p),s,mid), Build(rs(p),mid+1,e);
Tup(p);
}
void add(int l,int r,int p=1,int s=1,int e=2*n) {
if(l<=s&&e<=r) {
tr[p]++, tag[p]++;
return;
}
Tdn(p);
int mid=(s+e)>>1;
if(l<=mid) add(l,r,ls(p),s,mid);
if(r>mid) add(l,r,rs(p),mid+1,e);
Tup(p);
}
void count(int l,int r,int p=1,int s=1,int e=2*n) {
if(l<=s&&e<=r) {
if(tr[p]<Rmq) Rmq=tr[p], Jury=cnt[p];
else if(tr[p]==Rmq) Jury+=cnt[p];
return;
}
Tdn(p);
int mid=(s+e)>>1;
if(l<=mid) count(l,r,ls(p),s,mid);
if(r>mid) count(l,r,rs(p),mid+1,e);
}
void mian() {
memset(res,0,sizeof(res));
cin >> n;
up(i,1,n) cin >> a[i] >> b[i], t[a[i]]=i, t[b[i]]=-i;
build(), stk[top=1]=0, updata(top);
up(i,1,2*n) {
if(t[i]>0) {
pre[t[i]]=lower(t[i]);
stk[++top]=t[i], updata(top);
}
else stk[top]=inf, updata(top--);
}
Build();
up(i,1,n) {
Jury=0, Rmq=inf, count(a[i],b[i]);
res[i]=(b[i]-a[i]+1-Jury)/2, add(a[i],b[i]);
}
int ans=f[n]; a[0]=0, b[0]=2*n+1;
cout << ans << ' ';
up(i,1,n) {
int j=pre[i];
(ans*=g[(b[j]-a[j]-1)/2-res[j]])%=P;
res[j]+=(b[i]-a[i]+1)/2-res[i];
(ans*=f[(b[j]-a[j]-1)/2-res[j]])%=P;
(ans*=f[(b[i]-a[i]-1)/2-res[i]])%=P;
cout << ans << ' ';
} cout << '\n';
}
signed main() {
freopen("bracket.in","r",stdin);
freopen("bracket.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
n=1e5, mul[0]=inv[0]=inv[1]=1;
up(i,1,2*n) mul[i]=mul[i-1]*i%P;
up(i,2,2*n) inv[i]=inv[P%i]*(P-P/i)%P;
up(i,2,2*n) inv[i]=inv[i-1]*inv[i]%P;
up(i,0,n) f[i]=ksm(i+1)*C(2*i,i)%P, g[i]=ksm(f[i]);
cin >> id >> T;
while(T--) mian();
return 0;
}
连通图connect
原来 int 一个函数不写返回值但是没有用来运算也可能会 re。
\([1,p],(p,n]\) 答案独立可以分开处理,下面考虑怎么做前缀。只有加边的话可以考虑对操作分块,询问按 \(p\) 排序,双指针并查集加前面的块边,然后对于每个查询额外加不超过 \(\sqrt n\) 条边,并查集/dfs 就可以做了。
不是我喜欢的删除直接不删,对于块中要删除但以前已经加了的边,考虑最开始不加入改成加边操作即可。
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
using namespace std;
const int N=100005;
int Pluto, n, m, q, B, tot, o[N], x[N], y[N], id[N], tag[N], len, Ans[N], vis[N], X[N], Y[N];
pii u[N];
map<pii,int> sav;
vector<int> arr[N], to[N];
struct DSU {
int dsu[N], ans;
void clear() { ans=0; up(i,1,n) dsu[i]=i; }
int get(int x) { return x==dsu[x]?x:dsu[x]=get(dsu[x]); }
void merge(int x,int y) { /*cout << "Insert " << x << ' ' << y << '\n';*/ x=get(x), y=get(y); if(x!=y) dsu[x]=y, ++ans; }
void dfs(int x) { for(int y:to[x]) if(!vis[y]) vis[y]=1, dfs(y); }
int query(vector<int> e,int lim) {
int num=0, ret=0;
vector<pii> re;
for(int u:e) {
int l=X[u], r=Y[u];
if(lim>0&&(l>lim||r>lim)) continue;
if(lim<0&&(l<=-lim||r<=-lim)) continue;
l=get(l), r=get(r);
if(l!=r) {
if(!to[l].size()) ++num;
if(!to[r].size()) ++num;
to[l].pb(r), to[r].pb(l), re.pb(mp(l,r));;
}
}
// cout << "fuck " << lim << " : "; for(pii u:re) cout << '(' << u.first << ',' << u.second << ") "; cout << '\n';
for(pii u:re) {
int l=u.first, r=u.second;
if(!vis[l]) ++ret, vis[l]=1, dfs(l);
if(!vis[r]) ++ret, vis[r]=1, dfs(r);
}
for(pii u:re) {
int l=u.first, r=u.second;
vis[l]=vis[r]=0, to[l].clear(), to[r].clear();
}
return num-ret;
}
// void out() { cout << "dsu : "; up(i,1,n) cout << get(i) << ' '; cout << '\n'; }
} dsu;
struct QUR { int id, d, p; } xun[N], cha[N];
signed main() {
// freopen("wa.in","r",stdin);
freopen("connect.in","r",stdin);
freopen("connect.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> Pluto >> n >> m >> q, B=sqrt(m); // 记得改回来w
up(i,1,m) {
cin >> o[i] >> x[i] >> y[i];
if(y[i]<x[i]) swap(x[i],y[i]);
if(o[i]==0) id[i]=sav[mp(x[i],y[i])]=++tot, X[tot]=x[i], Y[tot]=y[i];
if(o[i]==1) id[i]=sav[mp(x[i],y[i])];
} sav.clear();
up(i,1,q) xun[i].id=i, cin >> xun[i].d >> xun[i].p;
sort(xun+1,xun+1+q,[](QUR A,QUR B){return A.d<B.d;});
int pos=1;
for(int l=1, r=B; l<=m; l=r+1, r=min(l+B-1,m)) {
// 操作求解 l~r
// cout << "Round " << l << ' ' << r << '\n';
memset(tag,0,sizeof(tag));
up(i,1,l-1) {
if(o[i]==0) tag[id[i]]=1;
if(o[i]==1) tag[id[i]]=0;
}
up(i,l,r) if(o[i]==1&&tag[id[i]]) tag[id[i]]=0, arr[l-1].pb(id[i]);
up(i,l,r) {
bool flag=1;
for(int u:arr[i-1]) if(u==id[i]) flag=0; else arr[i].pb(u);
if(flag) arr[i].pb(id[i]);
} // 最后记得 clear
len=0;
up(i,1,m) if(tag[i]) u[++len]=mp(X[i],Y[i]);
// up(i,1,len) cout << '(' << u[i].first << ',' << u[i].second << ") "; cout << '\n';
int Q=0, j;
while(pos<=q&&l<=xun[pos].d&&xun[pos].d<=r) cha[++Q]=xun[pos++];
sort(cha+1,cha+1+Q,[](QUR A,QUR B){return A.p<B.p;});
sort(u+1,u+1+len,[](pii A,pii B){return A.second<B.second;});
dsu.clear(), j=1;
up(i,1,Q) {
int d=cha[i].d, p=cha[i].p;
while(j<=len&&u[j].second<=p) dsu.merge(u[j].first,u[j].second), ++j;
// cout << "cha " << p << " : "; for(int f:arr[d]) cout << '(' << X[f] << ',' << Y[f] << ") "; cout << '\n';
// dsu.out();
Ans[cha[i].id]+=p-dsu.ans-dsu.query(arr[d],p);
// cout << "contribute = " << p-dsu.ans-dsu.query(arr[d],p) << '\n';
}
sort(u+1,u+1+len,[](pii A,pii B){return A.first >B.first ;});
dsu.clear(), j=1;
dn(i,Q,1) {
int d=cha[i].d, p=cha[i].p;
while(j<=len&&u[j].first>p) dsu.merge(u[j].first,u[j].second), ++j;
// cout << "Cha " << p << " : "; for(int f:arr[d]) cout << '(' << X[f] << ',' << Y[f] << ") "; cout << '\n';
// dsu.out();
Ans[cha[i].id]+=n-p-dsu.ans-dsu.query(arr[d],-p);
// cout << "contribute = " << n-p << ' ' << -dsu.ans << ' ' << -dsu.query(arr[d],-p) << '\n';
}
up(i,l-1,r) arr[i].clear();
}
up(i,1,q) cout << Ans[i]-1 << '\n';
return 0;
}