2023.8.12

lgj 放水场。

job

\(T\) 个单位时间内,每个单位时间 \(t\) 可以选择一个未选过的 \(i\) 且满足 \(b_i\ge t\),获得 \(a_i\) 的贡献。

求最大贡献。

\(n\le 2\times 10^6\)\(a_i,b_i\le T\le 10^9\).

考虑把 \(a\) 大的 \(i\) 放到前面,开一个 set,弄出来可行的最后一个单位时间,令这个单位时间选择 \(i\) 即可。

时间复杂度 \(O(n\log n)\).

#include<bits/stdc++.h>
#pragma GCC optimize(3,"Ofast","inline")
#define ll long long
#define N 2000010
#define sit set<int>::iterator
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int T,n,m;
struct data{
	int a,b;
	bool operator<(const data &x)const{
		return a==x.a?b>x.b:a>x.a;
	}
}t[N];
set<int>s;
ll ans;
int main(){
	T=read(),n=read();
	for(int i=1,a,b;i<=n;i++){
		b=read(),a=read();
		if(b>n)ans+=a;
		else t[++m]=(data){a,b};
	}
	for(int i=1;i<=n;i++)
		s.insert(i);
	sort(t+1,t+1+m);
	for(int i=1;i<=m;i++){
		sit it=s.upper_bound(t[i].b);
		if(it==s.begin())continue;
		--it,ans+=t[i].a,s.erase(*it);
	}
	printf("%lld\n",ans);
	
	return 0;
}

shop

\(n\) 种货币 \(\{v_n\}\)(可以重复),你手上分别有 \(\{c_n\}\) 张,购买价值为 \(M\) 的货物,店主的货币是无限的。

试最小化给钱和找钱的总货币数,输出这个值。

\(n\le 200\)\(M\le 10^5\)\(v_i\le 200\)\(c_i\le 20000\).

考虑 \(f_{i,j}\) 表示考虑了前 \(i\) 种货币凑出 \(j\) 的最小张数。

答案即 \(\min_{k}f_{n,k}+f_{n,k-M}\).

后者应该是完全背包但是我不知道为什么用这个多重背包数组过了。

时间复杂度 \(O(nM\log C)\).

#include<bits/stdc++.h>
#pragma GCC optimize(3,"Ofast","inline")
#define N 205
#define L 17
#define R 200010
#define inf 0x3f3f3f3f
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int n,M,tot;
int _v[N],_c[N];
int f[2][R],o;
struct data{
	int v,c;
}a[N*L];
int main(){
	n=read(),M=read();
	for(int i=1;i<=n;i++)_v[i]=read();
	for(int i=1;i<=n;i++)_c[i]=read();
	for(int i=1,v,c;i<=n;i++){
		v=_v[i],c=_c[i];
		for(int j=1;c;j<<=1){
			if(c>=j)a[++tot]=(data){v*j,j},c-=j;
			else a[++tot]=(data){v*c,c},c=0;
		}
	}
	memset(f,0x3f,sizeof(f)),f[o][0]=0;
	for(int i=1;i<=tot;i++){
		o^=1;
		for(int j=0;j<min(M*2,a[i].v);j++)f[o][j]=f[o^1][j];
		for(int j=a[i].v;j<M*2;j++)
			f[o][j]=min(f[o^1][j],f[o^1][j-a[i].v]+a[i].c);
	}
	int ans=inf;
	for(int i=M;i<M*2;i++)
		ans=min(ans,f[o][i]+f[o][i-M]);
	if(ans==inf)puts("-1");
	else printf("%d\n",ans);
	
	return 0;
}

magic

P5522 [yLOI2019] 棠梨煎雪

\(m\) 个长 \(n\) 的串,分别有 \(0,1\) 和通配符 \(?\),支持:

  • 询问 \([l,r]\) 的串有多少种变为同一个串的方式。

  • 修改一个串。

\(m\le 10^5+7\)\(q\le 10^6+7\)\(n\le 30\),输入字符串的总长度不超过 \(5\times 10^6\).

\(0\rightarrow01\)\(1\rightarrow10\)\(?\rightarrow11\).

把它们放进 bitset 里且起来即可。

时间复杂度 \(O(q\frac{n}{\omega}\log m)\).

#include<bits/stdc++.h>
#pragma GCC optimize(3,"Ofast","inline")
#define N 100010
#define M 30
#define bs bitset<M<<1>
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int gets(){
	char ch=getchar();
	while(ch!='0'&&ch!='1'&&ch!='?')ch=getchar();
	return ch=='0'?0:(ch=='1'?1:2);
}
bs b[N],t[N<<2];
#define ls p<<1
#define rs p<<1|1
void update(int p){
	t[p]=t[ls]&t[rs];
}
void build(int p,int l,int r){
	if(l==r){
		t[p]=b[l];
		return;
	}
	int mid=(l+r)>>1;
	build(ls,l,mid);
	build(rs,mid+1,r);
	update(p);
}
bs mdf,res;
void modify(int p,int x,int l,int r){
	if(l==r){
		t[p]=mdf;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid)modify(ls,x,l,mid);
	else modify(rs,x,mid+1,r);
	update(p);
}
bs query(int p,int L,int R,int l,int r){
	if(L<=l&&R>=r)return t[p];
	int mid=(l+r)>>1;
	if(R<=mid)return query(ls,L,R,l,mid);
	if(L>mid)return query(rs,L,R,mid+1,r);
	return query(ls,L,R,l,mid)&query(rs,L,R,mid+1,r);
}
int n,m,q;
int ans;
int main(){
	m=read(),n=read(),q=read();
	for(int i=1;i<=n;i++)
		for(int j=0,opt;j<m;j++){
			opt=gets();
			if(opt!=1)b[i].set(j*2);
			if(opt)b[i].set(j*2+1);
		}
	build(1,1,n);
	for(int opt,l,r,x,cnt;q;q--){
		opt=read();
		if(!opt){
			l=read(),r=read();
			res=query(1,l,r,1,n),cnt=0;
			for(int i=0;i<m;i++){
				if(!res[i*2]&&!res[i*2+1]){cnt=-1;break;}
				cnt+=(res[i*2]&&res[i*2+1]);
			}
			if(cnt!=-1)ans^=(1<<cnt);
		}
		else{
			x=read();
			for(int j=0,opt;j<m;j++){
				mdf.reset(j*2),mdf.reset(j*2+1),opt=gets();
				if(opt!=1)mdf.set(j*2);
				if(opt)mdf.set(j*2+1);
			}
			modify(1,x,1,n);
		}
	}
	printf("%d\n",ans);
	
	return 0;
}

peace

在树上选出 \(m\) 条长度不超过 \(k\) 的链 \((x,y),x\le y\),且两条链在端点处不交,树上任意点都不被 \(m\) 条链同时覆盖。

可以认为 \(m\) 条链都有编号。

求总方案数对 \(998244353\) 取模的值。

四档极限分:

  1. \(n\le 10^6\)\(m\le 10^3\)\(k=0\).

  2. \(n\le 10^6\)\(m\le 10^3\)\(k=n-1\).

  3. \(n\le 10^6\)\(m\le 10^3\)\(k\le n\),树为一条链。

  4. \(n\le 10^3\)\(m\le 10^9\)\(k\le n\).

Part1

用所有的方案减去不合法的方案,合法的就是长度 \(\le k\) 的路径条数的 \(m\) 次方。

不合法的方案路径一定有交,枚举深度最浅的交点。

\(f(p)\) 为经过 \(p\) 且路径两端都在 \(p\) 子树内的长度不超过 \(k\) 的方案数,\(g(p)\) 为经过 \(p\) 且路径两端分别在 \(p\) 子树内外,长度不超过 \(k\) 的方案数。

\(p\) 为深度最浅的节点的不合法方案数为

\[\sum_{i=0}^{m-1}\binom{m}{i}g(p)^if(p)^{m-i} \]

即选出 \(i\) 个放到外面,且不能只放 \(m\) 在外面,否则最浅的交点非 \(p\).

时间复杂度 \(O(nm)\).

Part2

想一下怎么计算 \(f\)\(g\).

  • \(O(n^2)\) 方法

对于 \(q\in\operatorname{son}(p)\),递归 \(q\) 的子树,用前缀桶里的信息更新答案,再把新数据扔进桶里,容易计算 \(f\).

计算 \(g(p)\),把子树 \(p\) 以外的看成 \(p\) 的另一颗子树,用类似的方法计算。

  • \(k=0\)

链长为 \(0\)\(f(p)=1\)\(g(p)=0\).

时间复杂度 \(O(n)\).

  • \(k=n-1\)

链可以走遍每个点对,设 \(\displaystyle h(x)=\binom{x}{2}+x\),即在 \(x\) 个点中选出 \(2\) 个点满足终点不小于起点的方案数。

\[f(p)=h(\operatorname{siz}(p))-\sum_{q\in\operatorname{son}(p)}h(\operatorname{siz}(q)) \]

\[g(p)=\operatorname{siz}(p)(n-\operatorname{siz}(p)) \]

时间复杂度 \(O(n)\).

  • 树为一条链

\(f(p)\) 等价与直接向后扩展,和 \(k+1\)\(\min\).

\(g(p)\) 就是前面能扩展的向后扩展,对 \(f(p)\) 作前缀和,再减掉到达不了的 \(f(p)\).

Part3

但是 \(O(nm)\) 的复杂度不支持通过前三档极限分。

再看眼那个求和式

\[\sum_{i=0}^{m-1}\binom{m}{i}g(p)^if(p)^{m-i} \]

这个就是二项式定理的形式,即

\[\sum_{i=0}^{m}\binom{m}{i}g(p)^if(p)^{m-i}-g(p)^m \]

\[=(f(p)+g(p))^m-g(p)^m \]

时间复杂度 \(O(n\log m)\).

最后的答案其实是可以容斥出来的。

code

没标程慢慢写。

posted @ 2023-08-12 15:56  SError  阅读(24)  评论(0编辑  收藏  举报