2023.3.20 日寄

2023.3.20 日寄

模拟赛

\(~~~~\) 最近怎么回事啊啊啊。爆编,交错代码,剪枝-90,还能干吗啊啊啊!

小B的班级

题意

\(~~~~\) 给一棵 \(n\) 个点的树,边带权,上面有黑白点各 \(m\) 个,定义其权值为所有黑白点配对方案中每一对距离的和最大的一个。求树的期望权值。
\(~~~~\) \(1\leq n,m\leq 2500\).

题解

\(~~~~\) 非常显然的是按边考虑贡献,那么设边某一侧分别有 \(x,y\) 个黑、白点,那它就最大贡献 \(\min(x,n-x)+\min (y,n-y)\) 次。这个东西可以合起来:\(\min(x+y,2n-x-y)\),那直接枚举 \(x+y\) ,就可以 \(\mathcal{O(nm)}\) 做了。记得乘上每个点不一样的组合数。

代码
查看代码
#include <bits/stdc++.h>
#define ll long long
#define PII pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std;
template<typename T>void read(T &x)
{
    T sig=1;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')sig=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    x*=sig;
}
const int MOD=1e9+7;
inline int Add(int a,int b){return (a+b)%MOD;}
inline int Dec(int a,int b){return (a-b+MOD)%MOD;}
inline int Mul(int a,int b){return 1ll*a*b%MOD;}
inline int qpow(int a,int b)
{
	int ret=1;
	while(b)
	{
		if(b&1) ret=Mul(ret,a);
		b>>=1;a=Mul(a,a);
	}
	return ret;
}
int n,m,Ans=0;
int Pow1[5005],Pow2[5005],Fac[5005],Inv[5005];
inline int C(int N,int r){return r>N?0:Mul(Fac[N],Mul(Inv[r],Inv[N-r]));}
int f[5005],Siz[5005];
vector <PII> G[5005];
void Update(int v,int x)
{
	Pow1[0]=Pow2[0]=1;
	for(int i=1;i<=2*m;i++) Pow1[i]=Mul(Pow1[i-1],x),Pow2[i]=Mul(Pow2[i-1],n-x);
	for(int i=0;i<=2*m;i++) f[i]=Add(f[i],Mul(Mul(v,Pow1[i]),Pow2[2*m-i]));
}
void dfs(int u,int fa)
{
	Siz[u]=1;
	for(int i=0;i<(int)G[u].size();i++)
	{
		int v=G[u][i].first,w=G[u][i].second;
		if(v==fa) continue;
		dfs(v,u); Siz[u]+=Siz[v]; Update(w,Siz[v]);
	}
}
int main() {
	Fac[0]=1;
	for(int i=1;i<=5000;i++) Fac[i]=Mul(Fac[i-1],i);
	Inv[5000]=qpow(Fac[5000],MOD-2);
	for(int i=4999;i>=0;i--) Inv[i]=Mul(Inv[i+1],i+1);
	read(n);read(m);
	for(int i=1,u,v,w;i<n;i++)
	{
		read(u);read(v);read(w);
		G[u].push_back(mp(v,w));
		G[v].push_back(mp(u,w));
	}
	dfs(1,0);
	Ans=0;
	for(int i=0;i<=m;i++)
		for(int j=0;j<=m;j++) Ans=Add(Ans,Mul(Add(min(i,m-j),min(j,m-i)),Mul(Mul(C(m,i),C(m,j)),f[i+j])));
	printf("%d",Ans);
	return 0;
}
/*
瑶草一何碧,春入武陵溪。溪上桃花无数,花上有黄鹂。我欲穿花寻路,直入白云深处,浩气展虹霓。只恐花深里,红露湿人衣。
坐玉石,欹玉枕。拂金徽。谪仙何处,无人伴我白螺杯。我为灵芝仙草,不为朱唇丹脸,长啸亦何为。醉舞下山去,明月逐人归。

最近在搞什么啊啊啊啊
三场爆两次编交错一次 
*/

小B的环

题意

\(~~~~\) 给一个环形的字符串,求是否对于 \(k\in[0,n-1]\),删除 \(k\) 个连续字符可以使得字符串当中不存在连续相同字符。
\(~~~~\) \(1\leq \sum n\leq 10^7\).

题解

\(~~~~\) 首先破环为链倍长,非常套路。

\(~~~~\) 然后考虑对于倍长过后的串我们就只需要删一个前缀和一个后缀,看有没有满足条件的长度。很显然当两个字符本来就相邻的时候你把原串从这里断开处理没有问题,然后就取所有断出来的串的并就好了,但是现在需要做的只是判断首尾字符了。

\(~~~~\) 于是就变成怎么判断一个串能不能删多少个,当 \(a[1,n-k]=a[k+1,n]\) 的时候删这个长度显然不行,于是hash判断就好了。

代码

\(~~~~\) 出题人不会造数据放过 \(n^2\) 了。

查看代码
#include <bits/stdc++.h>
#define ll long long
#define PII pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std;
template<typename T>void read(T &x)
{
    T f=1;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    x*=f;
}
char Ans[10000005],S[10000005];
bool vis[10000005];
int lst[10000005],Diff[10000005],pos[200];
void Add(int a,int b,int Val=1){Diff[a]+=Val;Diff[b+1]-=Val;}
int main() {
//	freopen("loop.in","r",stdin);
//	freopen("loop.out","w",stdout);
	while(~scanf("%s",S+1))
	{
		bool Same=true;
		int len=strlen(S+1);Ans[len]=0;
		for(int i=1;i<=len;i++) S[i+len]=S[i],Same&=(S[i]==S[1]);
		if(Same)
		{
			for(int i=0;i<len;i++) Ans[i]='0';
			puts(Ans);
			continue;
		}
		len<<=1;
		for(int i=1;i<=26;i++) pos[i]=0; Diff[0]=0;
		for(int i=1;i<=len;i++) lst[i]=pos[S[i]-'a'+1],pos[S[i]-'a'+1]=i,Diff[i]=0;
		for(int i=1;i<=len;i++)
		{
			int j=i;
			while(j+1<=len&&S[j+1]!=S[j]) j++;
			for(int k=j;k>=i;k--)
			{
//				if(len>10000&&vis[k]) continue;
				int x=k;Add(2,k-i+1);
				while(lst[x]>=i)
				{
					vis[x]=true;
					Add(k-lst[x]+1,k-lst[x]+1,-1);
					x=lst[x];
				}
			}
			i=j;
		}
		len>>=1;
		for(int i=1;i<=len;i++) Diff[i]+=Diff[i-1];
//		for(int i=1;i<=len;i++) printf("%d:%d ",i,Diff[i]);
		for(int i=1;i<=len;i++) 
			Ans[len-i]=(Diff[i]>0)?'1':'0';
		puts(Ans);
		for(int i=0;i<=(len<<1);i++) Ans[i]=S[i]=lst[i]=Diff[i]=vis[i]=0;
	}
	return 0;
}
/*
瑶草一何碧,春入武陵溪。溪上桃花无数,花上有黄鹂。我欲穿花寻路,直入白云深处,浩气展虹霓。只恐花深里,红露湿人衣。
坐玉石,欹玉枕。拂金徽。谪仙何处,无人伴我白螺杯。我为灵芝仙草,不为朱唇丹脸,长啸亦何为。醉舞下山去,明月逐人归。

喜报:
1.出题人不会造数据 
2.sb剪枝WA了 
*/

小B的农场

题意

\(~~~~\) 给出一个左下角 \((0,0)\) ,右上角 \((W,H)\) 的坐标轴区域,里面有 \(n\) 个点。你需要选一个矩形,满足其内部没有点(边界可以有) ,其周长最大。
\(~~~~\) \(1\leq W,H\leq 10^8,1\leq n\leq 3\times 10^5\).

题解

\(~~~~\) 注意两个性质即可做:

\(~~~~\) 第一个:答案的矩形一定过横轴或竖轴的中线。因为答案至少一定可以取 \((W+1)\times 2\)\((H+1)\times 2\),那最后只占四分之一的小地方也就 \(W+H\) ,显然不优。(啊啊啊,这是做过的套路啊)

\(~~~~\) 第二个:答案的矩形一定四边上都有点。因为如果有一边没有那把这边不断扩展直到有一个点在其中间一定合法且更优。

\(~~~~\) 那么我们就可以固定一个轴,在另一个上扫描下端点,线段树维护上端点,然后左右两端单调栈维护即可。

代码
查看代码
#include <bits/stdc++.h>
#define ll long long
#define PII pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std;
template<typename T>void read(T &x)
{
    T f=1;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    x*=f;
}
int W,H,n,toty;
int X[300005],Y[300005];
int brry[300005],Minn[300005];
int L[300005],R[300005];
struct SegmentTree{
	#define ls p<<1
	#define rs p<<1|1
	#define lson p<<1,l,mid
	#define rson p<<1|1,mid+1,r 
	int Tag[1200005],tr[1200005];
	inline void pushUp(int p){tr[p]=max(tr[ls],tr[rs]);}
	void Build(int p,int l,int r)
	{
		Tag[p]=0;
		if(l==r){tr[p]=-brry[l-1];return;}
		int mid=(l+r)>>1;
		Build(lson); Build(rson);
		pushUp(p);
	}
	inline void pushTag(int p,int x){tr[p]+=x;Tag[p]+=x;}
	inline void pushDown(int p)
	{
		if(Tag[p]) pushTag(ls,Tag[p]),pushTag(rs,Tag[p]),Tag[p]=0;
	}
	void Modify(int p,int l,int r,int lx,int rx,int Val)
	{
		if(lx<=l&&r<=rx) return pushTag(p,Val);
		int mid=(l+r)>>1; pushDown(p);
		if(lx<=mid) Modify(lson,lx,rx,Val);
		if(mid<rx)  Modify(rson,lx,rx,Val);
		pushUp(p);
	}
	int Query(int p,int l,int r,int lx,int rx)
	{
		if(lx<=l&&r<=rx) return tr[p];
		int mid=(l+r)>>1,ret=-1e9;pushDown(p);
		if(lx<=mid) ret=max(ret,Query(lson,lx,rx));
		if(mid<rx)  ret=max(ret,Query(rson,lx,rx));
		return ret;
	}
	#undef ls
	#undef rs
	#undef lson
	#undef rson
}Seg;
int StaL[300005],StaR[300005],TopL,TopR;
inline int Solve()
{
	int MID=W>>1;toty=0;
	for(int i=1;i<=n;i++) brry[++toty]=Y[i]; brry[++toty]=H;
	sort(brry+1,brry+1+toty);
	toty=unique(brry+1,brry+1+toty)-brry-1;
	for(int i=1;i<=toty;i++)  L[i]=MID,R[i]=W-MID;
	for(int i=1;i<=n;i++)
	{
		int p=lower_bound(brry+1,brry+1+toty,Y[i])-brry;
		if(X[i]<=MID) L[p]=min(L[p],MID-X[i]);
		else R[p]=min(R[p],X[i]-MID);
	}
	Seg.Build(1,1,toty);int ret=0;TopL=TopR=0;
	for(int i=1;i<=toty;i++)
	{
		ret=max(ret,brry[i]-brry[i-1]+W);
		if(i>1) ret=max(ret,Seg.Query(1,1,toty,1,i-1)+brry[i]);
		while(TopL&&L[StaL[TopL]]>=L[i]) Seg.Modify(1,1,toty,StaL[TopL-1]+1,StaL[TopL],-L[StaL[TopL]]),TopL--;
		Seg.Modify(1,1,toty,StaL[TopL]+1,i,L[i]); StaL[++TopL]=i;
		
		while(TopR&&R[StaR[TopR]]>=R[i]) Seg.Modify(1,1,toty,StaR[TopR-1]+1,StaR[TopR],-R[StaR[TopR]]),TopR--;
		Seg.Modify(1,1,toty,StaR[TopR]+1,i,R[i]); StaR[++TopR]=i;
	}
	return ret<<1;
}
int main() {
	read(W);read(H);read(n);
	for(int i=1;i<=n;i++)
	{
		read(X[i]),read(Y[i]);
		if(X[i]==0||X[i]==W||Y[i]==0||Y[i]==H) i--,n--;
	}
	int Ans=Solve();
	swap(W,H);
	for(int i=1;i<=n;i++) swap(X[i],Y[i]);
	Ans=max(Ans,Solve());
	printf("%d",Ans);
	return 0;
}
/*
瑶草一何碧,春入武陵溪。溪上桃花无数,花上有黄鹂。我欲穿花寻路,直入白云深处,浩气展虹霓。只恐花深里,红露湿人衣。
坐玉石,欹玉枕。拂金徽。谪仙何处,无人伴我白螺杯。我为灵芝仙草,不为朱唇丹脸,长啸亦何为。醉舞下山去,明月逐人归。

10 10 4
1 6
4 1
6 9
9 4
*/

鲜花

\(~~~~\) 这几天事情真的很多啊啊啊,上周四、五运动会题都没怎么补,星期六月赛讲评 (+修苦力怕农场),星期天有组模拟赛的一堆东西。(到时候把我一堆拙劣仿写的文字贴上来吧)

\(~~~~\) 还有就是,朱慈的知乎真的太精彩了,每一篇文字都有浓浓的现实主义与荒谬风,简直卡夫卡啊啊啊!

posted @ 2023-03-20 19:43  Azazеl  阅读(37)  评论(0编辑  收藏  举报