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
\(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\) 取模的值。
四档极限分:
-
\(n\le 10^6\),\(m\le 10^3\),\(k=0\).
-
\(n\le 10^6\),\(m\le 10^3\),\(k=n-1\).
-
\(n\le 10^6\),\(m\le 10^3\),\(k\le n\),树为一条链。
-
\(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\) 为深度最浅的节点的不合法方案数为
即选出 \(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\) 个点满足终点不小于起点的方案数。
时间复杂度 \(O(n)\).
- 树为一条链
\(f(p)\) 等价与直接向后扩展,和 \(k+1\) 取 \(\min\).
\(g(p)\) 就是前面能扩展的向后扩展,对 \(f(p)\) 作前缀和,再减掉到达不了的 \(f(p)\).
Part3
但是 \(O(nm)\) 的复杂度不支持通过前三档极限分。
再看眼那个求和式
这个就是二项式定理的形式,即
时间复杂度 \(O(n\log m)\).
最后的答案其实是可以容斥出来的。
code
没标程慢慢写。