USACO20FEB P

Delegation P

将树的 \(n-1\) 条边划分为若干链,问最大的 \(k\),使得这些链长度均 \(\ge k\)

\(n\le 10^5\)


先二分。

由于一个点到自己父亲只有一条边,肯定要传一条最长的上去。然后考虑当前子树:

  • 对根,从最小的开始匹配,二分找到最小的满足的,找不到则不合法。

  • 对其他,若发现有不合法的,先记录下来因为可以往上传,找到第二个则不合法。

时间复杂度 \(O(n\log^2 n)\)

点击查看代码
#include<bits/stdc++.h>
#define N 100010
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;vector<int>e[N];
int f[N];bool flag;
void dfs(int u,int fa,int l){
	if(flag)return;
	multiset<int>m;
	for(int v:e[u]){
		if(v==fa)continue;
		dfs(v,u,l);
		m.insert(f[v]+1);
	}
	if((u==1&&(m.size()&1))||(u!=1&&!(m.size()&1)))m.insert(0);
	bool cur=false;
	while(!m.empty()){
		auto it1=m.begin();
		m.erase(it1);
		auto it2=m.lower_bound(l-*it1);
		if(u==1){
			if(it2==m.end())return flag=true,void();
			m.erase(it2);
		}
		else{
			if(it2==m.end()){
				if(cur)return flag=true,void();
				cur=true,f[u]=*it1;
			}
			else m.erase(it2);
		}
	}
}
bool check(int x){
	memset(f,0,sizeof(f)),flag=false;
	dfs(1,0,x);
	return !flag;
}
int main(){
	n=read();
	for(int i=1,u,v;i<n;i++){
		u=read(),v=read();
		e[u].push_back(v),e[v].push_back(u);
	}
	int l=1,r=n,mid,ans=1;
	while(l<=r){
		mid=(l+r)>>1;
		if(check(mid))ans=mid,l=mid+1;
		else r=mid-1;
	}
	printf("%d\n",ans);

	return 0;
}

Equilateral Triangles P

\(n\times n\) 的方阵中有若干点,两点的距离为它们的曼哈顿距离。问等边三角形的数量。

\(n\le 300\)


曼哈顿距离意义下,每个正三角形 \(ABC\) 都存在一点外心 \(O\),使得 \(OA,OB\) 分别与 \(x,y\) 轴平行,且 \(OA=OB=OC\)

长这样:

那么 \(C\)\(AB\) 关于 \(O\) 的对称线段上,斜着做前缀和即可,旋转四次分别做。

防止算重有细节。时间复杂度 \(O(n^3)\)

点击查看代码
#include<bits/stdc++.h>
#define N 905
#define V 900
#define _ 300
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,ans,s[N][N];
char c[N][N],tp[N][N];
void solve(){
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			tp[j+_][n-i+1+_]=c[i+_][j+_];
	memset(s,0,sizeof(s)),memcpy(c,tp,sizeof(c));
	for(int i=1;i<=V;i++)
		for(int j=1;j<=i;j++)
			s[j][i+1-j]=s[j-1][i+2-j]+(c[j][i+1-j]=='*');
	for(int i=2;i<=V;i++)
		for(int j=i;j<=V;j++)
			s[j][i+V-j]=s[j-1][i+V+1-j]+(c[j][i+V-j]=='*');
	for(int i=_+1;i<=_+n;i++)
		for(int j=_+1;j<=_+n;j++)
			for(int k=j-1;k>_&&i-j+k>_;k--){
				if(c[i][k]=='.'||c[i-j+k][j]=='.')continue;
				ans+=s[i+j-k][j]-s[i][2*j-k];
			}
}
int main(){
	n=read();
	for(int i=_+1;i<=_+n;i++)
		scanf("%s",c[i]+_+1);
	for(int i=1;i<=4;i++)solve();
	printf("%d\n",ans);

	return 0;
}

Help Yourself P

\(n\) 条线段,问所有线段的 \(2^n\) 个子集中,区间覆盖后的连通块数量的 \(k\) 次方之和模 \(10^9+7\)

\(n\le 10^5\)\(2\le k\le 10\)。所有 \(l_i\)\(r_i\) 形成 \(1\)\(2n\) 的一个排列。


考虑 \(k=1\) 的情况,就是 Help Yourself G

将所有线段按左端点升序排序。考虑加入 \([l,r]\) 的答案的贡献。

\(f_i\) 表示右端点为 \(i\) 的线段对答案产生的贡献。

  • 对于右端点在 \([1,l-1]\) 的,与 \([l,r]\) 无交,连通块数加一,将 \(f_1,\dots,f_{l-1}\)\(1\) 后累加至 \(f_r\)

  • 对于右端点在 \([l,r-1]\) 的,与 \([l,r]\) 相交,连通块数不变,直接累加至 \(f_{r}\)

  • 对于右端点在 \([r+1,2n]\) 的,其包含 \([l,r]\),此时有效的右端点在这些线段上,不能用于 \(f_r\),应把贡献累积到 \(f_{r+1}\)\(f_{2n}\),就是乘上 \(2\)

写一个支持单点改、区间查的线段树即可。


\(k\ne 1\) 时遇到的问题是 \(f_1\)\(f_{l-1}\)\(+1\) 该怎么处理。

二项式定理,\(\displaystyle (x+1)^k=\sum_{i=0}^{k}{k\choose i}x^i\),分别维护 \(x^0\)\(x^k\) 即可。时间复杂度 \(O(nk^2+nk\log n)\)

点击查看代码
#include<bits/stdc++.h>
#define N 100010
#define M 11
#define P 1000000007
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;
}
struct seg{
	int l,r;
	bool operator<(const seg &t)const{
		return l<t.l;
	}
}s[N];
int k,C[M][M];
struct node{
	int dat[11];
	node(){for(int i=0;i<=k;i++)dat[i]=0;}
};
#define dat(x,y) t[x].val.dat[y]
#define tag(x) t[x].tag
struct Tree{
	node val;int tag;
}t[N<<3];
#define ls p<<1
#define rs p<<1|1
void build(int p,int l,int r){
	if(l==r)return;
	int mid=(l+r)>>1;
	build(ls,l,mid);
	build(rs,mid+1,r);
}
void pushup(int p){
	for(int i=0;i<=k;i++)
		dat(p,i)=(dat(ls,i)+dat(rs,i))%P;
}
void pushdown(int p){
	if(tag(p)==1)return;
	for(int i=0;i<=k;i++){
		dat(ls,i)=1ll*dat(ls,i)*tag(p)%P;
		dat(rs,i)=1ll*dat(rs,i)*tag(p)%P;
	}
	tag(ls)=1ll*tag(ls)*tag(p)%P;
	tag(rs)=1ll*tag(rs)*tag(p)%P;
	tag(p)=1;
}
void add(int p,int l,int r,int x,node v){
	if(l==r){
		for(int i=0;i<=k;i++)
			dat(p,i)=(dat(p,i)+v.dat[i])%P;
		return;
	}
	pushdown(p);
	int mid=(l+r)>>1;
	if(x<=mid)add(ls,l,mid,x,v);
	else add(rs,mid+1,r,x,v);
	pushup(p);
}
void mul(int p,int l,int r,int L,int R,int v){
	if(L>R)return;
	if(L<=l&&r<=R){
		tag(p)=1ll*tag(p)*v%P;
		for(int i=0;i<=k;i++)
			dat(p,i)=1ll*dat(p,i)*v%P;
		return;
	}
	int mid=(l+r)>>1;
	pushdown(p);
	if(L<=mid)mul(ls,l,mid,L,R,v);
	if(R>mid)mul(rs,mid+1,r,L,R,v);
	pushup(p);
}
node query(int p,int l,int r,int L,int R){
	if(L>R){return node();}
	if(L<=l&&r<=R)return t[p].val;
	int mid=(l+r)>>1;node ret;
	pushdown(p);
	if(L<=mid)ret=query(ls,l,mid,L,R);
	if(R>mid){
		node tp=query(rs,mid+1,r,L,R);
		for(int i=0;i<=k;i++)
			ret.dat[i]=(ret.dat[i]+tp.dat[i])%P;
	}
	return ret;
}
int n,lim;
int main(){
	n=read(),lim=n*2,k=read();
	C[0][0]=1;
	for(int i=1;i<=k;i++){
		C[i][0]=C[i][i]=1;
		for(int j=1;j<i;j++)
			C[i][j]=C[i-1][j-1]+C[i-1][j];
	}
	build(1,0,lim);
	node ori;
	ori.dat[0]=1,add(1,0,lim,0,ori);
	for(int i=1;i<=n;i++)
		s[i].l=read(),s[i].r=read();
	sort(s+1,s+1+n);
	node cur,tp;
	for(int o=1,l,r;o<=n;o++){
		l=s[o].l,r=s[o].r;
		tp=query(1,0,lim,0,l-1);
		for(int i=0;i<=k;i++){
			cur.dat[i]=0;
			for(int j=0;j<=i;j++)
				cur.dat[i]=(cur.dat[i]+1ll*C[i][j]*tp.dat[j])%P;
		}
		tp=query(1,0,lim,l,r-1);
		for(int i=0;i<=k;i++)
			cur.dat[i]=(cur.dat[i]+tp.dat[i])%P;
		add(1,1,lim,r,cur);
		mul(1,1,lim,r+1,lim,2);
	}
	printf("%d\n",dat(1,k));
	
	return 0;
}
posted @ 2024-02-28 20:31  SError  阅读(7)  评论(0编辑  收藏  举报