模拟赛20200210【扫描线(单调性),最小割,基环树染色(树哈希+Polya)】

T1:

在这里插入图片描述
在这里插入图片描述

题解:

在这里插入图片描述
C类点可以不用平衡树维护,因为B类点的 a [ i ] a[i] a[i]一定是后缀最小值,所以只需要在询问的时候把 a [ j ] < a [ i ] a[j]<a[i] a[j]<a[i]的点的贡献全部删掉就可以了。在计算C类点的贡献时只需要将 a [ j ] a[j] a[j]到前缀最大值区间加1就可以了。
考试的时候想到单调性但是搞来搞去没搞出来(被“浙江省选模拟赛”标题吓住导致轻易自闭。。),n^2维护一个前缀和走人。。
Code:

#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
int n,a[maxn],up[maxn],id[maxn],ans;
bool low[maxn];
int add[maxn<<2],mx[maxn<<2];
inline void upd(int i,int v){add[i]+=v,mx[i]+=v;}
inline void pushdown(int i){
	if(add[i]) upd(i<<1,add[i]),upd(i<<1|1,add[i]),add[i]=0;
}
void ins(int i,int l,int r,int x,int y,int v){
	if(x<=l&&r<=y) {upd(i,v);return;}
	int mid=(l+r)>>1; pushdown(i);
	if(x<=mid) ins(i<<1,l,mid,x,y,v);
	if(y>mid) ins(i<<1|1,mid+1,r,x,y,v);
	mx[i]=max(mx[i<<1],mx[i<<1|1]);
}
bool cmp(int i,int j){return a[i]<a[j];}
int main()
{
	freopen("grass.in","r",stdin);
	freopen("grass.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),id[i]=i;
	sort(id+1,id+1+n,cmp);
	for(int i=n,mn=1e9;i>=1;i--) if(a[i]<mn) mn=a[i],low[i]=1;
	for(int i=1,Mx=0,j=1;i<=n;i++){
		up[i]=Mx=max(Mx,a[i]);
		ins(1,1,n,a[i],up[i],1);
		if(low[i]){
			for(;j<=n&&a[id[j]]<a[i];j++) ins(1,1,n,a[id[j]],up[id[j]],-1);
			ans=max(ans,mx[1]);
		}
	}
	printf("%d\n",ans);
}

另一种做法是基于决策单调性,越靠右的A类点选择的B类点也越靠右,画个图观察一下变化就可以看出,于是可以分治+主席树询问矩形点数,复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)


T2:

在这里插入图片描述
在这里插入图片描述

题解:

不开心度写成那样摆明了是要拆点。
感知一下可以发现只要满足 r 1 = 0 ,   ∣ r u − r v ∣ ≤ 1 r_1=0,~{|r_u-r_v|\le1} r1=0, rurv1的方案就是可行的。
然后就暴力dfs每个点的 r r r
在这里插入图片描述
神乎其技的最小割。
转化为 u ( i , j ) u(i,j) u(i,j)表示第 i i i 个点到首都的实际距离大于 j j j
∣ d u − d v ∣ ≤ 1 |d_u-d_v|\le 1 dudv1 相当于 d u > k d_u>k du>k为真时, d v > k − 1 d_v>k-1 dv>k1必须为真; d v > k d_v>k dv>k为真时, d u > k − 1 d_u>k-1 du>k1必须为真;
因为 d 1 = 0 d_1=0 d1=0,所以 u ( i , j ) u(i,j) u(i,j)连向 u ( i , j + 1 ) u(i,j+1) u(i,j+1)的边流量都是 inf ⁡ \inf inf,除了1对应的末尾之外的点不需要向 T T T连边,因为不可能被割。

Code:

#include<bits/stdc++.h>
#define maxn 65
#define maxm 15005
using namespace std;
const int inf = 0x3f3f3f3f;
int n,m,S,T,cnt,id[maxn][maxn],t[maxn];
int fir[maxn*maxn],cur[maxn*maxn],nxt[maxm],to[maxm],c[maxm],tot=1;
inline void line(int x,int y,int z){
	nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,c[tot]=z;
	nxt[++tot]=fir[y],fir[y]=tot,to[tot]=x,c[tot]=0;
}
inline int Cost(int x){return int(10*log(233*x*x+1));}
namespace Maxflow{
	const int N = 3605;
	int d[N],vd[N],sz;
	int aug(int u,int augco){
		if(u==T) return augco;
		int delta,need=augco;
		for(int &i=cur[u];i;i=nxt[i]) if(c[i]&&d[u]==d[to[i]]+1){
			delta=aug(to[i],min(c[i],need)),c[i]-=delta,c[i^1]+=delta;
			if(!(need-=delta)||d[S]==sz) return augco-need;
		}
		if(!--vd[d[u]]) d[S]=sz;
		else vd[++d[u]]++,cur[u]=fir[u];
		return augco-need;
	}
	int SAP(){
		int flow=0; sz=T+1,memcpy(cur,fir,sz<<2);
		while(d[S]<sz) flow+=aug(S,inf);
		return flow;
	}
}
int main()
{
	freopen("road.in","r",stdin);
	freopen("road.out","w",stdout);
	scanf("%d%d",&n,&m),S=0;
	for(int i=1;i<=n;i++) scanf("%d",&t[i]);
	for(int i=1;i<=n;i++) for(int j=0;j<n;j++) id[i][j]=++cnt;
	T=cnt+1;
	line(S,id[1][0],Cost(t[1]-0));
	for(int i=1;i<n;i++) line(id[1][i-1],id[1][i],inf);
	line(id[1][n-1],T,inf);
	for(int i=2;i<=n;i++){
		line(S,id[i][0],Cost(t[i]-0));
		for(int j=1;j<n;j++) line(id[i][j-1],id[i][j],Cost(t[i]-j));
	}
	for(int i=1,x,y;i<=m;i++){ 
		scanf("%d%d",&x,&y);
		for(int j=1;j<n;j++) line(id[x][j],id[y][j-1],inf),line(id[y][j],id[x][j-1],inf);
	}
	printf("%d\n",Maxflow::SAP());
}
T3:

在这里插入图片描述
在这里插入图片描述

题解:

就是这道题
树哈希的方法参见代码,计算同构方案数时考虑 k k k个同构的儿子放进 f [ v ] f[v] f[v]种颜色的盒子里,方案数为 C f [ v ] + k − 1 f [ v ] − 1 C_{f[v]+k-1}^{f[v]-1} Cf[v]+k1f[v]1
注意Polya定理中的置换必须使得染色方案 c c c经过置换之后也存在。所以这题的置换一定是循环节的倍数。

Code:

#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
typedef unsigned long long ULL;
const int mod = 1e9+7, Seed = 19260817;
int n,m,ans,cir[maxn],stk[maxn],top,son[maxn],fac[maxn],inv[maxn],f[maxn],fail[maxn];
bool onc[maxn],vis[maxn];
ULL H[maxn];
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot=1;
inline void line(int x,int y){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y;}
inline int Pow(int a,int b){
	int s=1; for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) s=1ll*s*a%mod;
	return s;
}
inline int C(int n,int m){return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;}
int gcd(int a,int b){return b?gcd(b,a%b):a;}
void dfs(int u,int ff){
	vis[u]=1,stk[++top]=u;
	for(int i=fir[u],v;i;i=nxt[i]) if(i^1^ff){
		if(!vis[v=to[i]]) {dfs(v,i);if(cir[0]) return;}
		else {do onc[cir[++*cir]=stk[top]]=1; while(stk[top--]!=v);return;}
	}
	top--;
}
bool cmp(int i,int j){return H[i]<H[j];}
void solve(int u,int ff){
	for(int i=fir[u],v;i;i=nxt[i]) if(!onc[v=to[i]]&&v!=ff) solve(v,u);
	son[0]=0;
	for(int i=fir[u],v;i;i=nxt[i]) if(!onc[v=to[i]]&&v!=ff) son[++*son]=v;
	sort(son+1,son+1+*son,cmp);
	f[u]=m,H[u]=2333333;
	for(int i=1,v,j,k;i<=*son;i=j){
		v=son[i];
		for(j=i,k=0;j<=*son&&H[son[j]]==H[v];k++,j++) H[u]=H[u]*Seed^H[son[j]];
		int ret=0;
		for(int F=f[v],x=1;x<=min(f[v],k);F=1ll*F*(f[v]-x)%mod,x++)
			ret=(ret+1ll*F*inv[x]%mod*C(k-1,x-1))%mod;
		f[u]=1ll*f[u]*ret%mod;
	}
	H[u]=H[u]*Seed*Seed;
}
int main()
{
	freopen("color.in","r",stdin);
	freopen("color.out","w",stdout);
	scanf("%d%d",&n,&m);
	fac[0]=fac[1]=inv[0]=inv[1]=1;
	for(int i=2;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod,inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(int i=2;i<=n;i++) inv[i]=1ll*inv[i]*inv[i-1]%mod;
	for(int i=1,x;i<=n;i++) scanf("%d",&x),line(x,i),line(i,x);
	dfs(1,0);
	for(int i=1;i<=*cir;i++) solve(cir[i],0);
	fail[0]=-1;
	for(int i=0,j=-1;i<*cir;fail[++i]=++j)
		while(j!=-1&&H[cir[i+1]]!=H[cir[j+1]]) j=fail[j];
	int num=*cir,len=num-fail[num];
	if(!fail[num]||num%len){
		ans=1;
		for(int i=1;i<=num;i++) ans=1ll*ans*f[cir[i]]%mod;
		printf("%d\n",ans);
	}
	else{
		m=1,num/=len,ans=0;
		for(int i=1;i<=len;i++) m=1ll*m*f[cir[i]]%mod;
		for(int i=1;i<=num;i++) ans=(ans+Pow(m,gcd(i,num)))%mod;
		printf("%d\n",1ll*ans*Pow(num,mod-2)%mod);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值