骄傲 孔雀羽翎上的暗槽 从最肮脏裂缝开凿 被爱意和现实击倒 停止创造

test10

大清洁(clr)

枚举 \(m\),因为小于调和级数,所以可以暴力扫每个位置计算初始计划表每个位置填法有 \(1/2\) 种。问题只剩下怎么去重,从小到大算出每个 \(m\) 恰好的方案数,就好容斥了,是简单的减法。

#pragma GCC optimize(1,2,3,"Ofast","inline")
#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)

using namespace std;

const int N=100005, P=998244353;

int n, ans, coel[N];
char str[N];

signed main() {
	freopen("clr.in","r",stdin);
	freopen("clr.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> (str+1);
	up(m,1,n-1) if(n%m==0) {
		int res=1;
		up(i,1,m) {
			bool flag=1; 
			up(j,1,n/m) if(str[(j-1)*m+i]=='.') flag=0;
			if(flag) res=2*res%P;
		}
		(coel[m]+=res)%=P, (ans+=coel[m])%=P;
		up(i,2,n/m) (coel[m*i]-=coel[m])%=P;
	}
	cout << (ans%P+P)%P << '\n';
	return 0;
}

最长子串(str)

其实做过 P3590 并且记得做法,注意到多加几个肯定很容易满足条件,左右拿三个进去扫,然后反证是过程多但不难的。

但是来都来了我们再胡一个对脑子更稳定的做法,考虑 \(a_i=A_i-B_i,b_i=B_i-C_i,c_i=C_i-A_i\),其中 \(A/B/C\) 是前缀和,然后你可以从 \((a/b/c)_l\neq (a/b/c)_r\)\(l\) 转移到 \(r\),发现记录答案最大的 \((a,b,c)\) 不同的三元组和其答案就可以惹。

#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) 
 
using namespace std;

const int N=10000005;

int n, ans=1; char s[N];

void solve(int l) {
	int res=0, C=0, B=0, S=0;
	up(i,l,n) {
		C+=(s[i]=='N');
		B+=(s[i]=='O');
		S+=(s[i]=='I');
		if(C!=B&&B!=S&&C!=S) ans=max(ans,i-l+1);
	}
}

signed main() {
	freopen("str.in","r",stdin);
	freopen("str.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> (s+1);
	solve(1), solve(2), solve(3);
	up(i,1,n/2) swap(s[i],s[n-i+1]);
	solve(1), solve(2), solve(3);
	cout << ans;
	return 0;
}

熬鹰杯(cup)

sort(&a[1],&a[n]); -> sort(&a[1],&a[n+1]);

题目会自然形成连通块个场馆,我们考虑怎么对一个连通块进行操作。考虑给相交的连边,你可以每次摘除生成树的叶子,所以操作方式显然是按照连通块大小从小到大删除连通块,所以相等大小连通块带来一个 \(\prod_{siz=1}^n cnt[siz]!\) 的贡献,之后每个连通块是独立子问题。

我们考虑怎么对一个连通块计数,你要寻找不破坏连通性的删比赛顺序,这个好难记状态的,不妨时间倒流变成更好做的寻找不破坏连通性的加比赛顺序,暴力 dp 大概是一个什么 \(f[i][l,r]\) 表示考虑到 \(i\) 轮并集是 \([l,r]\) 的方案数,转移是扩展 \([l,r]\) 或者钦定用 \(\times (cnt[l,r]-i)\) 的贡献去增加 \(i\)

这个第二种贡献方式太拉跨了,考虑一下怎么优化,先观察加入时没有扩展区间的比赛怎么贡献,找到第一个覆盖的时刻,从那时候开始随便找一个时间加入,哦那这个是弱化版的树上拓扑排序方案数,可以用 \(n!\prod\frac{1}{siz_u}\) 来计算,发现一个扩展的比赛跟上一个拓展的比赛树直接划开,子树大小的贡献刚好是 \(n-cnt[l_{pre}+1,r_{pre}-1]\),那么可以 dp 了,设 \(f[l,r]\) 表示并集为 \([l,r]\) 的拓扑系数,用类似前缀和的东西优化即可。

所以说有时候看着 dp 不知道怎么下手的时候不妨看看全局的贡献情况,心里更有底一点。

#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define ll 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
#define fir first
#define sec second

using namespace std;

const int N=4005, P=1e9+7;

int mul[N], inv[N], f[N][N], mid[N][N], cnt[N][N];
int L[N][N], R[N][N], s[N][N], Ans=1, ran[N], sav=1;
vector<int> lr[N], rl[N];
pii p[N];

inline void Add(int &a,int b) { a=(a+b)%P; }
inline void Mul(int &a,int b) { a=(ll)a*b%P; }

void solve(int n) {
	
	Mul(sav,++ran[n]);
	
	up(i,1,n) {
		++mid[p[i].fir][p[i].sec];
		lr[p[i].fir].pb(p[i].sec);
		rl[p[i].sec].pb(p[i].fir);
		Add(f[p[i].fir][p[i].sec],mul[n-1]);
	}
	n<<=1;
	up(r,1,n) {
		dn(l,r,1) cnt[l][r]=cnt[l+1][r]+mid[l][r];
		up(l,1,r) cnt[l][r]=cnt[l][r]+cnt[l][r-1];
	}
	up(len,2,n) {
		up(l,1,n-len+1) {
			int r=l+len-1;
			if(mid[l][r]) Add(f[l][r],s[l+1][r-1]);
			for(int u:lr[l]) {
				if(u>=r) continue;
				Add(f[l][r],(R[l+1][r]-R[u+1][r])%P);
			}
			for(int u:rl[r]) {
				if(u<=l) continue;
				Add(f[l][r],(L[l][r-1]-L[l][u-1])%P);
			}
			if(len<n) Mul(f[l][r],inv[n/2-cnt[l][r]]);
			L[l][r]=(L[l][r-1]+f[l][r])%P;
			R[l][r]=(R[l+1][r]+f[l][r])%P;
			s[l][r]=(s[l+1][r]+L[l][r])%P;
		}
	}
	Mul(Ans,f[1][n]);
	
	up(i,1,n) up(j,1,n) f[i][j]=mid[i][j]=L[i][j]=R[i][j]=s[i][j]=0;
	up(i,1,n) lr[i].clear(), rl[i].clear();
	
}

signed main() {
	
	freopen("cup.in","r",stdin);
	freopen("cup.out","w",stdout);
	
	ios::sync_with_stdio(0);
	cin.tie(0);
	
	int n; cin >> n;
	vector<pii> a(n+1);
	up(i,1,n) cin >> a[i].fir >> a[i].sec;
	
	mul[0]=inv[0]=inv[1]=1;
	up(i,1,n) mul[i]=(ll)mul[i-1]*i%P;
	up(i,2,n) inv[i]=(ll)inv[P%i]*(P-P/i)%P;
	
	sort(&a[1],&a[n+1]);
	for(int l=1, r=1; l<=n; l=r+1, r=l) {
		int lim=a[l].sec;
		while(r<n&&a[r+1].fir<lim) lim=max(lim,a[++r].sec);
		up(i,l,r) p[i-l+1]=mp(a[i].fir-a[l].fir+1,a[i].sec-a[l].fir+1);
		solve(r-l+1);
	}
	cout << ((ll)Ans*sav%P+P)%P << '\n';
	
	return 0;
}

朴素数据结构题(mex)

\(\text{mex}\) 一贯先转成从正整数,感觉会漂亮一些,然后 \(\text{mex}\geq u\) 的条件就是 \(u\leq \sum[a_i\leq u]\)

考虑单组怎么做,你要考虑二维平面不同高度的差分(这里指的是主席树/前缀和的差分,或者说这一段横坐标)的点数是否大于高度,这个东西混乱无序感觉根本没办法上技巧云云,只能暴力按照 \(ans\downarrow\) 去扫,动态记录有多少个 \(\leq ans\) 的数。

那我们考虑怎么一起做这个暴力,先想想怎么快速给包含 \(u\) 的区间计数减 \(1\)。只有相交是好做的,对排序后的一段区间有贡献,只有包含也是好做的,可以求出超区间的答案之后再加入该区间。这两个是很容易无机结合一下的,就是细节很多,给个大概的实现框架,讨论部分细节(

  1. 树状数组,用于加入区间的时候快速查询 \(\sum_{i=l}^r [a_i\leq ans]\)
  2. 排序+双指针,用于扫询问和扫 \(u\)
  3. 线段树,维护还未加入的区间,要做快速查找新的不被包含区间,发现可以在 \(r>i\) 的部分查找 \(l\) 最小的部分并判断是否 \(l<j\) 然后更新 \(i\),那么线段树二分一下,还要做动态加删。
  4. set,维护加入的区间,要快速查找一些需要的端点。
  5. 线段树,维护询问,存在的部分左右端点都是递增的但是因为前面按照 \(r\uparrow\) 那干脆一开始重构了编号用这个,维护区间加和查找全局最大值。
#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define ll 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
#define l first
#define r second
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)

using namespace std;

const int N=500005, inf=1e9;

int n, T, a[N], pr[N], rp[N], Ans[N];
pii sa[N], sb[N], b[N];

struct BIT {
	int tr[N];
	void add(int x,int v) { for( ; x<=n; x+=x&-x) tr[x]+=v; }
	int ask(int x) { int res=0; for( ; x; x-=x&-x) res+=tr[x]; return res; }
	int ask(int l,int r) { return ask(r)-ask(l-1); }
} arr;
struct SUB {
	int tr[N<<2], tag[N<<2];
	inline void tup(int p) { tr[p]=max(tr[ls(p)],tr[rs(p)]); }
	inline void tdn(int p) {
		tr[ls(p)]+=tag[p], tag[ls(p)]+=tag[p];
		tr[rs(p)]+=tag[p], tag[rs(p)]+=tag[p];
		tag[p]=0;
	}
	void build() { up(i,1,4*T) tr[i]=-inf; }
	void add(int x,int p=1,int s=1,int e=T) {
		if(s==e) return tr[p]=arr.ask(b[s].l,b[s].r), void();
		tdn(p);
		int mid=(s+e)>>1;
		x<=mid?add(x,ls(p),s,mid):add(x,rs(p),mid+1,e);
		tup(p);
	}
	void del(int x,int p=1,int s=1,int e=T) {
		if(s==e) return tr[p]=-inf, void();
		tdn(p);
		int mid=(s+e)>>1;
		x<=mid?del(x,ls(p),s,mid):del(x,rs(p),mid+1,e);
		tup(p);
	}
	void modify(int l,int r,int p=1,int s=1,int e=T) {
		if(l<=s&&e<=r) return --tr[p], --tag[p], void();
		tdn(p);
		int mid=(s+e)>>1;
		if(l<=mid) modify(l,r,ls(p),s,mid);
		if(r>mid) modify(l,r,rs(p),mid+1,e);
		tup(p);
	}
	int query(int lim,int p=1,int s=1,int e=T) {
		if(tr[p]<lim) return 0;
		if(s==e) return s;
		tdn(p);
		int mid=(s+e)>>1;
		return tr[ls(p)]>=lim?query(lim,ls(p),s,mid):query(lim,rs(p),mid+1,e);
	}
	void check(int p=1,int s=1,int e=T) {
		if(s==e) { cout << tr[p] << ' '; return; }
		tdn(p);
		int mid=(s+e)>>1;
		check(ls(p),s,mid);
		check(rs(p),mid+1,e);
	}
} Q;
struct Grood {
	int tl[N], tr[N];
	multiset<int> L, R;
	void build() { L.insert(0), L.insert(n+1), R.insert(0), R.insert(n+1); tl[n+1]=tr[n+1]=T+1; }
	inline pii query(int x) { return mp(tr[*R.lower_bound(x)],tl[*--L.upper_bound(x)]); }
	inline void add(int u) { int l=b[u].l, r=b[u].r; tl[l]=tr[r]=u, L.insert(l), R.insert(r); }
	inline void del(int u) { int l=b[u].l, r=b[u].r; L.erase(L.find(l)), R.erase(R.find(r)); }
	inline int pre(int u) { return *--R.lower_bound(b[u].r); }
	inline int suf(int u) { return *  L.lower_bound(b[u].l); }
} god;
struct Baaad {
	int tr[N<<2];
	inline void tup(int p) {
		if(!tr[ls(p)]||!tr[rs(p)]) tr[p]=tr[ls(p)]|tr[rs(p)];
		else tr[p]=b[tr[ls(p)]].l<b[tr[rs(p)]].l?tr[ls(p)]:tr[rs(p)];
	}
	void build(int p=1,int s=1,int e=T) {
		if(s==e) return tr[p]=s, void();
		int mid=(s+e)>>1;
		build(ls(p),s,mid), build(rs(p),mid+1,e);
		tup(p);
	}
	void modify(int x,int p=1,int s=1,int e=T) {
		if(s==e) return tr[p]=0, void();
		int mid=(s+e)>>1;
		x<=mid?modify(x,ls(p),s,mid):modify(x,rs(p),mid+1,e);
		tup(p); 
	}
	int query(int low,int p=1,int s=1,int e=T) {
		if(!tr[p]||low<=b[s].r) return tr[p];
		int mid=(s+e)>>1, l=0, r=query(low,rs(p),mid+1,e);
		if(low<=b[mid].r) l=query(low,ls(p),s,mid);
		return (!l||!r)?(l|r):(b[l].l<b[r].l?l:r);
	}
} bad;

void insert(int L,int R) {
	int u;
	while(u=bad.query(L+1)) {
		if(b[u].l>=R) break;
		Q.add(u), god.add(u), bad.modify(u), L=b[u].r;
	}
}

signed main() {
	freopen("mex.in","r",stdin);
	freopen("mex.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> T;
	up(i,1,n) cin >> a[i], sa[i]=mp(a[i]=min(a[i],n)+1,i);
	up(i,1,T) pr[i]=i, cin >> sb[i].l >> sb[i].r;
	sort(pr+1,pr+1+T,[](int i,int j){return sb[i].r<sb[j].r;});
	up(i,1,T) b[i]=sb[pr[i]], rp[pr[i]]=i;
	sort(sa+1,sa+1+n,[](pii i,pii j){return i.l>j.l;});
	up(i,1,n) arr.add(i,1);
	Q.build(), god.build(), bad.build();
	insert(0,n+1); 
	int o=1;
	dn(ans,n+1,0) {
		int u;
		while(u=Q.query(ans)) {
			Ans[pr[u]]=ans;
			god.del(u), Q.del(u);
			insert(god.pre(u),god.suf(u));
		}
		while(o<=n&&sa[o].l==ans) {
			 int u=sa[o++].r;
			 arr.add(u,-1);
			 pii seg=god.query(u);
			 if(seg.l&&seg.r&&seg.l<=seg.r) Q.modify(seg.l,seg.r);
		}
	}
	up(i,1,T) cout << Ans[i] << '\n';
	return 0;
}
posted @ 2025-10-09 22:08  Hypoxia571  阅读(5)  评论(0)    收藏  举报