USACO23FEB P
P9130 [USACO23FEB] Hungry Cow P
考虑楼房重建,其实是类似于一个线段树二分。
对于每个区间维护有多少个空位,有多少超出的部分,区间内的答案是多少,然后直接楼房重建即可。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
const int mod=1e9+7;
const int inf=1e9;
#define ll long long
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define pb push_back
inline int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int n,m,a[maxn],tot;
ll X[maxn],P[maxn],len[maxn];int Y[maxn];
struct node{
ll siz,ept,ans,chao,tot;
}tr[maxn<<2];
#define INF 1e18
const int iv2=(mod+1)/2;
inline ll calc(ll l,ll r){
return (l+r)%mod*((r-l+1)%mod)%mod*iv2%mod;
}
#define lc tr[h<<1]
#define rc tr[h<<1|1]
inline void build(int h,int l,int r){
tr[h].tot=calc(P[l],P[r+1]-1);
if(l==r){
tr[h].ept=len[l];
return;
}int mid=(l+r)>>1;
build(h<<1,l,mid);
build(h<<1|1,mid+1,r);
tr[h].ept=lc.ept+rc.ept;
}
inline void print(node x){
printf("ans=%lld ept=%lld chao=%lld\n",x.ans,x.ept,x.chao);
}
inline node query(int h,int l,int r,ll x){
node res;
if(l==r){
res.ans=calc(P[l],P[l]+x+tr[h].siz-1);
res.chao=0;res.ept=tr[h].ept-x;
// print(res);
return res;
}int mid=(l+r)>>1;
if(lc.ept>=x){
// printf("qry h=%d l=%d r=%d x=%lld case1\n",h,l,r,x);
node ls=query(h<<1,l,mid,x);
res.ans=(tr[h].ans-lc.ans+ls.ans+mod)%mod;
// res.ans=(ls.ans+rc.ans)%mod;
res.chao=rc.chao;
res.ept=ls.ept+rc.ept;
// print(res);print(rc);
return res;
}else{
// printf("qry h=%d l=%d r=%d x=%lld case2\n",h,l,r,x);
node rs=query(h<<1|1,mid+1,r,x-lc.ept+lc.chao);
res.ans=(lc.tot+rs.ans)%mod;
res.chao=rs.chao;res.ept=rs.ept;
return res;
}
}
inline void update(int h,int l,int r){
// if(h==1)print(lc);
if(!lc.chao){
tr[h].ept=lc.ept+rc.ept;
tr[h].chao=rc.chao;
tr[h].ans=(lc.ans+rc.ans)%mod;
return;
}
if(lc.chao>=rc.ept){
tr[h].ept=lc.ept;
tr[h].chao=rc.chao+lc.chao-rc.ept;
tr[h].ans=(lc.ans+rc.tot)%mod;
return;
}
int mid=(l+r)>>1;
node res=query(h<<1|1,mid+1,r,lc.chao);
tr[h].ept=lc.ept+rc.ept-lc.chao;
tr[h].chao=rc.chao;
tr[h].ans=(lc.ans+res.ans)%mod;
// if(h==1){
// print(res);
// }
}
inline void modify(int h,int l,int r,int x,int y){
if(l==r){
tr[h].siz=y;
tr[h].ept=max(0ll,len[l]-y);
tr[h].chao=max(0ll,y-len[l]);
tr[h].ans=calc(P[l],min(P[l+1]-1,P[l]+y-1));
// printf("h=%d l=%d r=%d\n",h,l,r);
// print(tr[h]);
return;
}int mid=(l+r)>>1;
if(mid>=x)modify(h<<1,l,mid,x,y);
else modify(h<<1|1,mid+1,r,x,y);
update(h,l,r);
// printf("h=%d l=%d r=%d\n",h,l,r);
// print(tr[h]);
}
int main(){
// freopen("A.in","r",stdin);
// freopen("A.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)
scanf("%lld%d",X+i,Y+i),P[i]=X[i];
sort(P+1,P+1+n);
tot=unique(P+1,P+1+n)-P-1;P[tot+1]=INF;
// for(int i=1;i<=tot;i++)
// printf("%lld ",P[i]);puts("");
for(int i=1;i<=tot;i++)len[i]=P[i+1]-P[i];
for(int i=1;i<=n;i++)
X[i]=lower_bound(P+1,P+1+tot,X[i])-P;
build(1,1,tot);
for(int i=1;i<=n;i++){
// printf("%d %d\n",X[i],Y[i]);
modify(1,1,tot,X[i],Y[i]);
printf("%lld\n",tr[1].ans%mod);
}
return 0;
}
P9131 [USACO23FEB] Problem Setting P
大概是要做一个类似于半在线卷积的东西,只是在集合意义下。
考虑每次做完一层 popcount,然后再对这一层进行 FWT 之类的操作。
于是 \(O(2^mm^2)\),然而我场上 \(O(3^m)\) 通过了此题。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+10;
const int mod=1e9+7;
const int inf=1e9;
#define ll long long
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define pb push_back
#define ppc(x) __builtin_popcount(x)
inline int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int n,m,a[maxn],c[maxn],v[maxn],dp[21][1<<20];
int fac[maxn],inv[maxn],ifc[maxn],ans;
inline void add(int &x,int y){x=(x+y>=mod?x+y-mod:x+y);}
inline void FMT(int *f,int len){
for(int i=2;i<=len;i<<=1)
for(int j=0,p=i/2;j<len;j+=i)
for(int k=j;k<j+p;k++)add(f[k+p],f[k]);
}
int main(){
n=read(),m=read();
for(int i=0;i<m;i++){
for(int j=1;j<=n;j++){
char ch=getchar();
if(ch=='H')a[j]|=(1<<i);
}getchar();
}for(int i=1;i<=n;i++)c[a[i]]++;v[1]=1;
for(int i=2;i<=n;i++)v[i]=1ll*(1+v[i-1])*i%mod;
for(int i=0;i<=m;i++){
for(int j=0;j<(1<<m);j++)if(ppc(j)==i){
int sum=1;
for(int k=0;k<i;k++)add(sum,dp[k][j]);
dp[i][j]=1ll*sum*v[c[j]]%mod;
add(ans,dp[i][j]);
}FMT(dp[i],(1<<m));
}printf("%d\n",ans);
return 0;
}
P9132 [USACO23FEB] Watching Cowflix P
容易得出平方暴力,注意到答案是凸的且是 \(O(n)\) 级别,于是不同的斜率不超过根号种。
一个简单的想法是每次二分出斜率相同的连续段,但是带 \(\log\) 过不去,考虑把 \(\log\) 摊掉。
假设有斜率为 \(k\) 的长度为 \(l_k\),即 \(\sum kl_k=O(n)\),有基本事实 \(\sum \log l_k=O(\sqrt n)\)。
这个东西多年前我在谷群问过一次,是好证的(我懒得证了)。
于是我们先倍增再二分,就把 \(\log\) 摊掉了。
一个小的卡常技巧是可以先重标号一下原树,以便在 dp 时规避掉递归。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
const int mod=1e9+7;
const int inf=1e9;
#define ll long long
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define pb push_back
inline int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
char Str[maxn];
int n,m,a[maxn],dfn[maxn],ti,F[maxn],ok[maxn],CNT;
vector<int>G[maxn];
inline void dfs(int x,int fa){
dfn[x]=++ti;F[ti]=dfn[fa];
for(auto t:G[x])if(t^fa)dfs(t,x);
if(Str[x]=='1')ok[dfn[x]]=1,++CNT;
}
int Ans[maxn],dp[maxn][2];
inline int query(int x){
if(Ans[x])return Ans[x];
for(int i=1;i<=n;i++)dp[i][0]=dp[i][1]=0;
for(int i=n;i>=1;i--){
dp[i][1]++;
if(ok[i])dp[i][0]=inf;
dp[F[i]][0]+=min(dp[i][0],dp[i][1]+x);
dp[F[i]][1]+=min(dp[i][0],dp[i][1]);
}return Ans[x]=min(dp[1][0],dp[1][1]+x);
}
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n=read();scanf("%s",Str+1);
for(int i=1,x,y;i<n;i++)
x=read(),y=read(),G[x].pb(y),G[y].pb(x);
dfs(1,0);Ans[0]=CNT;//Ans[1]=2*CNT;
// for(int i=1;i<=n;i++)
// printf("%d ",query(i));puts("");
for(int l=1,r;l<=n;l=r+1){
int K=query(l)-query(l-1);
int val=query(l);
if(K==1){
for(int i=l+1;i<=n;i++)Ans[i]=Ans[i-1]+1;
break;
}int dt=1,L=l+1,R=n;
while(l+dt<=n){
if(query(l+dt)-val!=dt*K){
L=l+dt/2;R=l+dt;break;
}dt<<=1;
}r=L;
while(L<=R){
int mid=(L+R)>>1;
if(query(mid)-val==(mid-l)*K)r=mid,L=mid+1;
else R=mid-1;
}
for(int i=l+1;i<=r;i++)Ans[i]=Ans[i-1]+K;
}
for(int i=1;i<=n;i++)
printf("%d\n",Ans[i]);
return 0;
}
特别鸣谢叶开老师,我这三题都是对着他的博客学的。