我说 如果 若世上存在如果 如果我能说如果 愿接受所有的结果 和自我

test1

QOJ 9436

QOJ 12116

QOJ 10181

QOJ 8728

子集subset

枚举归类 \(T\) 来做不太现实,考虑分类枚举 \(S\) 计算贡献。现在需要一个双射,不妨令 \(S\) 的由 \(T\) 最大的 \(|S|-k\) 个来贡献。现在考虑枚举贡献的界限,做 dp 贡献进答案即可。

#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=3005, P=998244353;

int n, m, a[N], f[N], g[N], fib[N][N], Ans[N];
pii rk[N];

inline void add(int &a,int b) { a=(a+b)%P; }

signed main() {
	freopen("subset.in","r",stdin);
	freopen("subset.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	up(i,0,3000) {
		fib[i][0]=fib[i][i]=1;
		up(j,1,i-1) fib[i][j]=(fib[i-1][j-1]+fib[i-1][j])%P;
	}
	cin >> n >> m;
	up(i,1,n) {
		cin >> a[i];
		a[i]=min(a[i],m);
		rk[i]=mp(-a[i],i);
	}
	sort(rk+1,rk+1+n);
	f[0]=1;
	up(r,1,n) {
		int i=rk[r].second;
		int sum=0;
		up(j,m-a[i],m) add(sum,f[j]);
		up(j,0,n-r) add(Ans[j],fib[n-r][j]*sum%P);
		up(j,0,m) g[j]=f[j];
		up(j,0,m) {
			if(j+a[i]>m) add(f[m],g[j]);
			else add(f[j+a[i]],g[j]);
		}
	}
	up(i,0,n) cout << Ans[i] << ' ';
	return 0;
}

彷徨walk

考虑随级一棵树的性质,每个点的路径长度期望很小,不妨考虑以此为势能,倒序暴力更新即可。

#include<bits/stdc++.h>
#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 n, m, p[N], dis[N], tag[N], Ans;
vector<int> to[N];

void spfa(int s) {
	queue<int> q;
	q.push(s);
	while(q.size()) {
		int x=q.front(); q.pop();
		for(int y:to[x]) if(dis[x]+tag[x]<dis[y]) {
			dis[y]=dis[x]+tag[x];
			q.push(y);
		}
	}
}

signed main() {
	freopen("walk.in","r",stdin);
	freopen("walk.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m;
	up(i,1,n-1) cin >> p[i];
	while(m--) {
		int u, v;
		cin >> u >> v;
		to[u].pb(v);
		to[v].pb(u);
	}
	up(i,1,n-1) dis[i]=1e9, tag[i]=1; 
	spfa(n);
	up(u,1,n-1) {
		int i=p[u];
		tag[i]=0, spfa(i);
		Ans+=dis[i];
	}
	cout << Ans;
	return 0;
}

矩阵mat

考虑有 \(a\) 行和为 \(2\)\(b=n-a\) 行和为 \(1\)\(c\) 列和为 \(2\)\(d=m-c\) 列和为 \(1\)。因为有等式 \(a+b=n,c+d=m,2a+b=2c+d\),所以可以 \(O(n)\) 枚举 \(a\) 并算出 \(b,c,d\)

那么现在考虑一下怎么求出 \((a,b,c,d)\) 的答案,具体而言本可想要知道,对于每一列分配进 \(2/1\) 个行,有 \(c/d\) 个列分配 \(2/1\) 个行,最后有 \(a/b\) 行被分配到 \(2/1\) 个列的方案数。最开始我们会想独立出顺序 \(\binom{c+d}{c}\binom{a+b}{a}\) 然后考虑一下配对的方案数, 不妨按从小到的顺序给 \(c,d\) 配对,要填 \(c\) 个双框(要去单调) \(d\) 个单框,分配进编号 \(u_i(,u_j)\),双框限制显然是 \(\frac{(2a+b)!}{2^c}\) 可以解决的因为素很多独立的双射,现在重复的还有 \([ij][ij]\) 这种就素有双框的的分类方式一致,显然可以容斥让恰好转成钦定,嗯对就可以惹,复杂度素 \(O(n^2)\) 的。

#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=10005, P=1e9+7;

int n, m, Ans, mul[N], inv[N], inv2[N], out;

int C(int n,int m) {
	if(m<0||m>n||n<0) return 0;
	return mul[n]*inv[m]%P*inv[n-m]%P; 
}

signed main() {
	freopen("mat.in","r",stdin);
	freopen("mat.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	mul[0]=inv[0]=inv[1]=inv2[0]=1;
	up(i,1,10000) mul[i]=mul[i-1]*i%P;
	up(i,2,10000) inv[i]=inv[P%i]*(P-P/i)%P;
	up(i,1,10000) inv2[i]=inv2[i-1]*inv[2]%P;
	up(i,2,10000) inv[i]=inv[i-1]*inv[i]%P; 
	cin >> n >> m;
	up(a,0,n) {
		int Ans=0;
		int b=n-a, d=a+2*b-m, c=m-d;
		if(c<0||c>m||d<0||d>m) continue;
		up(t,0,min(b,d)) {
			int qwq=C(b,t)*C(d,t)%P*mul[t]%P*inv2[b+d-t]%P*mul[c+2*d-2*t]%P;
			if(t&1) (Ans-=qwq)%=P; else (Ans+=qwq)%=P;
		}
		Ans=Ans*C(n,a)%P*C(m,c)%P;
		out=(out+Ans)%P;
	}
	cout << (out%P+P)%P;
	return 0;
}

军队army

显然素先离线离散化扫描线,注意到相交的两个矩形一定有至少一个矩形存在一条与另一个矩形相交的竖边界,然后容易想到把编号塞到线段树上,这样子相交的点一定会存在一个时刻存在对应的点有祖先关系。

很好做并且能处理掉很多关系的素加删的时候暴力给并查集合并,树上的节点不为空就加入这个公共集合,假设 \(j\)\(i\) 的祖先,已经解决掉除去 \(+i,+j,-j,-i\) 的所有情况惹,考虑怎么特殊处理这种情况,嗯别人惹毛我我就毛茸茸地走开。我们考虑在 \(i\) 的祖先上打上「和 \(i\) 合并」的 tag,这样子会产生 \(O(\log n)\) 个标记,每次加入的 \(j\) 合并之后把 tag 删掉即可,这个其实素相当于给子树打上有没有合并过的标记然后没有合并过的暴力去合并。困扰本可的素 \(+i,+j_1,-j_1,+j_2,-j_2\) 的标记鼠惹怎么办,显然素记录一下子树的前继集合和子树大小即可。然后不难发现愚蠢的本可说的这些相当于智慧的 lgx 说的「维护一下子树是不是一个集合,不是的话暴力更新」。

#include<bits/stdc++.h>
#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 ls(p) (p<<1)
#define rs(p) (p<<1|1)

using namespace std;

const int N=8000005;

int n, m, dsu[N], sp[N], ran, gp[N], tr[N], cnt[N], sum[N];
struct node {
	int len, l, r, id; 
	bool operator<(const node &rhs) const {
		if(len!=rhs.len) return len<rhs.len;
		return id>rhs.id;
	}
} p[N];

int get(int x) {
	if(x==dsu[x]) return x;
	return dsu[x]=get(dsu[x]);
}

inline void merge(int x,int y) {
	x=get(x), y=get(y);
	if(x!=y) dsu[x]=y;
}

inline void tup(int p) {
	sum[p]=sum[ls(p)]+sum[rs(p)]+cnt[p];
	if(sum[ls(p)]&&sum[rs(p)]) {
		if(gp[ls(p)]&&gp[rs(p)]&&get(gp[ls(p)])==get(gp[rs(p)])) gp[p]=gp[ls(p)];
		else gp[p]=0;
	}
	else if(sum[ls(p)]) gp[p]=gp[ls(p)];
	else if(sum[rs(p)]) gp[p]=gp[rs(p)];
	else gp[p]=(cnt[p]?tr[p]:0);
}

void clear(int p,int s,int e,int id) {
	if(!sum[p]) return;
	if(gp[p]) { merge(gp[p],id); return; }
	if(cnt[p]) merge(tr[p],id);
	int mid=(s+e)>>1;
	clear(ls(p),s,mid,id);
	clear(rs(p),mid+1,e,id);
	tup(p);
}

void add(int l,int r,int id,int p=1,int s=1,int e=ran) {
	if(cnt[p]) merge(tr[p],id);
	if(l<=s&&e<=r) {
		clear(p,s,e,id);
		if(!sum[p]) gp[p]=id;
		sum[p]++, cnt[p]++, tr[p]=id;
		return;
	}
	int mid=(s+e)>>1;
	if(l<=mid) add(l,r,id,ls(p),s,mid);
	if(r>mid) add(l,r,id,rs(p),mid+1,e);
	tup(p);
}

void del(int l,int r,int id,int p=1,int s=1,int e=ran) {
	if(cnt[p]) merge(tr[p],id);
	if(l<=s&&e<=r) {
		sum[p]--, cnt[p]--;
		if(!sum[p]) gp[p]=0;
		return;
	}
	int mid=(s+e)>>1;
	if(l<=mid) del(l,r,id,ls(p),s,mid);
	if(r>mid) del(l,r,id,rs(p),mid+1,e);
	tup(p);
}

signed main() {
	freopen("army.in","r",stdin);
	freopen("army.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n;
	up(i,1,n) {
		int a, b, c, d;
		cin >> a >> b >> c >> d;
		p[++m]=(node){a,b,d,i};
		p[++m]=(node){c,b,d,-i};
		dsu[i]=i, sp[++ran]=b, sp[++ran]=d;
	}
	sort(sp+1,sp+1+ran);
	ran=unique(sp+1,sp+1+ran)-sp-1;
	sort(p+1,p+1+m);
	up(i,1,m) {
		p[i].l=lower_bound(sp+1,sp+1+ran,p[i].l)-sp;
		p[i].r=lower_bound(sp+1,sp+1+ran,p[i].r)-sp;
		if(p[i].id>0) add(p[i].l,p[i].r,p[i].id);
		else del(p[i].l,p[i].r,-p[i].id);
	}
	map<int,int> ret;
	int ovo=0;
	up(i,1,n) {
		int u=get(i);
		if(ret.find(u)==ret.end()) ret[u]=ovo++;
		u=ret[u];
		cout << u << ' ';
	}
	return 0;
}
posted @ 2025-09-11 19:02  Hypoxia571  阅读(25)  评论(0)    收藏  举报