noip模拟82(待补)

A. 魔法

随便写.

A_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS{
	#define ll int
	#define lf double
	#define ull unsigned ll
	#define mp make_pair
	#define lb lower_bound
	#define ub upper_bound
	#define lbt(x) ((x)&(-(x)))
	#define Fill(x,y) memset(x,y,sizeof(x))
	#define Copy(x,y) memcpy(x,y,sizeof(x))
	#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
	auto read=[]()->ll{
		ll w=0; bool cit=1; char ch;
		while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
		while(isdigit(ch)) w=(w<<3)+(w<<1)+(ch^48),ch=getchar();
		return cit?w:(-w);
	};
} using namespace BSS;

const ll N=1e6+21;

char ch[N];

ll A,B;
ll n,as,bs,ab,ans,tail;
ll val[N],pos[N],vis[N],in[N];
ll L[N],R[N];
ll pre[N][3];
struct I { ll w,id; } p[N];
vector<ll> vec[N];
signed main(){
	File(magic);
	n=read(),A=read(),B=read(),ab=A+B; scanf("%s",ch+1);
	for(ll i=1;i<=n;i++){
		val[i]=(ch[i]=='R' ? 1 : 2);
		(val[i]&1) ? as++ : bs++ ;
	}
	if(as*B!=bs*A) puts("NO"),exit(0);
	if((as+bs)%(A+B)) puts("NO"),exit(0);
	if(A and as%A) puts("NO"),exit(0);
	if(B and bs%B) puts("NO"),exit(0);
	as=0,bs=0,tail=0;
	for(ll i=1;i<=n;i++){
		pos[++tail]=i,pre[tail][1]=pre[tail-1][1],pre[tail][2]=pre[tail-1][2];
		(val[i]&1) ? (pre[tail][1]++) : (pre[tail][2]++) ;
		as=pre[tail][1]-pre[max(tail-ab,0)][1];
		bs=pre[tail][2]-pre[max(tail-ab,0)][2];
		if(as==A and bs==B){
			ans++;
			for(ll j=tail-ab+1;j<=tail;j++) vec[ans].push_back(pos[j]),vis[pos[j]]=ans;
			tail-=ab;
		}
	}
	puts("YES");
	printf("%d\n",ans);
	for(ll i=1;i<=n;i++){
		if(!in[vis[i]]){
			for(auto j : vec[vis[i]]) printf("%d ",j);
			in[vis[i]]=1; puts("");
		}
	}
	exit(0);
}

B. 连通性

发现这个题目一开始很难做,因为根本无从下手.

我们考虑一下 \(Floyed\) 算法的实现过程,大概就是有一部分点依靠中间点建立联通关系.

对于一些没有被中间点联通的点对来说,\(AttackedFloyed\) (以下简称 \(AF\))最终跑出来的还是本来就直接联通的点对.

同样设 \(1\) ~ \(n-m\) 的点为黑点,设 \(n-m+1\) ~ \(n\) 的点为白点.

所以我们可以直接发现,对于白点来说,ta 们之间的路径,一定是除了端点之外全部都是黑点.

所以我们就可以来跑 \(dp\) 了,需要用到围绕基准点的思想.

B_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS{
	#define ll long long int
	#define lf double
	#define ull unsigned ll
	#define mp make_pair
	#define lb lower_bound
	#define ub upper_bound
	#define lbt(x) ((x)&(-(x)))
	#define Fill(x,y) memset(x,y,sizeof(x))
	#define Copy(x,y) memcpy(x,y,sizeof(x))
	#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
	auto read=[]()->ll{
		ll w=0; bool cit=1; char ch;
		while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
		while(isdigit(ch)) w=(w<<3)+(w<<1)+(ch^48),ch=getchar();
		return cit?w:(-w);
	};
} using namespace BSS;

const ll N=105,mod=1e9+7;

ll g[N],pow2[N*N];
ll h[N][N],C[N][N],f[N][N];
auto ksm=[](ll a,ll b,ll c)->ll{
	ll w=1; a%=c;
	for(;b;b>>=1,a=a*a%c) if(b&1) w=w*a%c;
	return w%c;
};
auto PreWork=[]()->void{
	for(ll i=0;i<=100;i++){
		C[i][0]=1;
		for(ll j=1;j<=i;j++)
			C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
	}
	g[0]=0,pow2[0]=1;
	for(ll i=1;i<=10000;i++) pow2[i]=(pow2[i-1]<<1ll)%mod;
	for(ll i=1;i<=100;i++){
		g[i]=pow2[i*(i-1)>>1];
		for(ll j=1;j<=i-1;j++)
			g[i]=(g[i]-g[j]*pow2[(i-j)*(i-j-1)>>1]%mod*C[i-1][j-1]%mod+mod)%mod;
	}
	for(ll i=0;i<=100;i++){
		for(ll j=0;j<=100;j++){
			h[i][j]=g[j]*ksm(pow2[j]-1,i,mod)%mod*pow2[i*(i-1)>>1]%mod;
		}
	}
	f[1][0]=1,f[0][0]=1,f[1][1]=1;
	for(ll n=2;n<=100;n++){
		f[n][0]=pow2[n*(n-1)>>1];
		for(ll m=1;m<=n;m++){	
			for(ll i=1;i<=m;i++){
				f[n][m]=(f[n][m]+f[n-i][m-i]*C[m-1][i-1]%mod)%mod;
				for(ll j=0;j<=n-m;j++)
					f[n][m]=(f[n][m]+f[n-i-j][m-i]*C[m-1][i-1]%mod*C[n-m][j]%mod*h[i][j]%mod)%mod;
			}
		}
	}
};
signed main(){
	File(floyd);
	PreWork(); ll n,m;
	for(int Ts=read();Ts;Ts--) n=read(),m=read(),printf("%lld\n",f[n][m]);
	exit(0);
}

C. 矩形

首先很自然的就感觉这可能会是个笛卡尔树或者单调栈之类的结构.

我们发现我们如果暴力 \(O(n^2)\) 做的话,空间上也需要存储 \(O(n^2)\) 的数据,于是很难不排除全部存储的想法.

所以考虑排除一些没有用的东西的枚举和储存.

一开始只考虑了对于排名一定大于 \(R\) 的直接不存,也就是维护一个类似于堆的结构,发现还是不行.

观察到 \(R-L+1\le3e5\),发现这个数据范围是可以接受的,于是考虑也去排除排名小于 \(L\) 的.

一开始考虑了权值线段树,发现面积一定是由 \(a*h\) (底乘高) 构成的,发现 \(a\) 的范围只有 \(1~n\),感觉很可做.

想着想着就发现我们在这个算法下面是需要分别找到排名位于 \(L\) 和 \(R\) 的权值.

感觉这个东西就是用来二分做的,如果我们能够迅速找到这个权值,那么接下来的东西就都很好做了.

在这个思路下,很容易发现我们还需要在权值线段树下二分,发现复杂度在 \(O(n*logn*logW)\),于是开始入手.

手玩了一下,发现是个等差数列,线段树直接不需要了,于是复杂度又变成了 \((OlogW)\),从而就很好写了.

关于二分,我们可以选择二分出排名位于 \(L/R\) 的面积,然后枚举每个点,既然我们的面积已定,如果我们枚举每个点,以这个点为最低的点,从而使高确定,那么底的长度所不大于的限制应该也是确定的.

关于等差数列:(以下用 \(1\) 表示根的位置,用 \(0\) 表示左右儿子所在的区间)
\(eg\): \(00001000000000\)
很难不发现,以在这个区间且经过 \(1\) 的一段连续线段为底的矩形的高都是根的 \(h\) (由于是小根堆),且底的长度位于不同的长度也有不同的选择方案.
设 \(1\) 及其较短一侧(例子中为左侧)的长度为 \(mn\),\(1\) 及其较长一侧(例子中为右侧)的长度为 \(mx\),设当前的底的长度为 \(a\).
例子中的 \(mn\) 等于 \(5\),\(mx\) 等于 \(10\).
长度位于 \(mx+1\) ~ \(len\) 的 \(a\),发现最多只能选择 \(len-a+1\) 个,于是等差数列显然.
长度位于 \(mn+1\) ~ \(mx\) 的 \(a\),最多只能选择 \(mn\) 个,枚举左端点显然.
长度位于 \(1\) ~ \(mn\) 的 \(a\),最多只能选 \(a\) 个,同样等差数列显然.

于是我们二分的总复杂度就是 \((OlogW)\),找到了 \(L\) 和 \(R\) 分别位于的权值,那么填数也就很简单了,不再赘述.

C_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS{
	#define ll long long 
	#define lf double
	#define ull unsigned ll
	#define mp make_pair
	#define lb lower_bound
	#define ub upper_bound
	#define lbt(x) ((x)&(-(x)))
	#define Fill(x,y) memset(x,y,sizeof(x))
	#define Copy(x,y) memcpy(x,y,sizeof(x))
	#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
	auto read=[]()->ll{
		ll w=0; bool cit=1; char ch;
		while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
		while(isdigit(ch)) w=(w<<3)+(w<<1)+(ch^48),ch=getchar();
		return cit?w:(-w);
	};
} using namespace BSS;

#define ls (x<<1)
#define rs (x<<1|1)
const ll N=6e5+21,inf=1e15;

ll L,R;
ll m,n,cnt,vall,valr,tail,maxn;
ll bin[N<<1],stk[N];
struct I { ll l,r,w,mn,mx,len; } p[N];
auto calc=[](ll x,ll y)->ll{
	ll mx=p[x].mx,mn=p[x].mn,len=p[x].len,res=0;
	ll z,tmp; y=min(y,len);
	if(y>mx) z=y-mx,y=mx,res+=(mn-1+mn-z)*z/2;
	if(y>mn) z=y-mn,y=mn,res+=mn*z;
	if(y>0) z=y,y=0,res+=z*(z+1)/2;
	return res;
};
auto check=[](ll rkw)->ll{
	ll x,y,z,rks=0;
	for(ll i=1,len;i<=n;i++){
		len=rkw/p[i].w,rks+=calc(i,len);
	}
	return rks;
};
auto getval=[](ll x)->ll{
	ll l=0,r=maxn,mid,res;
	while(l<=r){
		mid=(l+r)>>1;
		if(check(mid)>=x) r=mid-1,res=mid;
		else l=mid+1;
	}
	return res;
};
signed main(){
	File(rectangle);
	n=read(); ll x,y,z,l,r,mn,mx;
	for(ll i=1;i<=n;i++){
		p[i].w=read(),p[i].l=i,p[i].r=i;
		while(tail and p[x=stk[tail]].w>p[i].w){
			p[x].r=i-1,tail--;
			p[i].l=min(p[i].l,p[x].l);
		}
		stk[++tail]=i;
	}
	while(tail) x=stk[tail--],p[x].r=n;
	// for(ll i=1;i<=n;i++) cout<<p[i].l<<' '<<p[i].r<<endl;
	for(ll i=1;i<=n;i++){
		p[i].len=p[i].r-p[i].l+1;
		maxn=max(maxn,p[i].w*p[i].len);
		p[i].mn=min(i-p[i].l+1,p[i].r-i+1);
		p[i].mx=max(i-p[i].l+1,p[i].r-i+1);
	}
	L=read(),R=read(),vall=getval(L),valr=getval(R);
	// printf("%lld\n%lld\n",vall,valr); 
	vall++,valr--;
	for(ll i=1;i<=n;i++){
		l=ceil(1.0*vall/p[i].w),r=min(valr/p[i].w,p[i].len);
		mn=p[i].mn,mx=p[i].mx;
		if(r>mx){
			for(ll j=max(mx+1,l);j<=r;j++){
				for(ll k=1;k<=p[i].len-j+1;k++){
					bin[++cnt]=j*p[i].w; // ~~~
				}
			}
			r=mx;
		}
		if(r<l) continue;
		if(r>mn){
			for(ll j=max(mn+1,l);j<=r;j++){
				for(ll k=1;k<=mn;k++) bin[++cnt]=j*p[i].w;
			}
			r=mn;
		}
		if(r<l) continue;
		if(r>0){
			for(ll j=max(1ll,l);j<=r;j++){
				for(ll k=1;k<=j;k++) bin[++cnt]=j*p[i].w;
			}
		}
	}
	// sort(bin+1,bin+1+cnt);
	// for(ll i=1;i<=cnt;i++) printf("%lld ",bin[i]); 
	// puts("");
	vall--;  
	for(ll i=1,lmt=check(vall)-L+1;i<=lmt;i++) bin[++cnt]=vall;
	for(ll i=1,lmt=R-check(valr);i<=lmt;i++) bin[++cnt]=valr+1;
	sort(bin+1,bin+1+cnt);
	for(ll i=1;i<=R-L+1;i++) printf("%lld ",bin[i]); 
	exit(0);
}

D. 排列

上一篇:CF1379F1 Chess Strikes Back (easy version)(鸽笼原理、线段树)


下一篇:AtCoder Beginner Contest 223 题解