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;
}

特别鸣谢叶开老师,我这三题都是对着他的博客学的。

posted @ 2023-05-11 20:06  syzf2222  阅读(123)  评论(0编辑  收藏  举报