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, ∣ru−rv∣≤1的方案就是可行的。
然后就暴力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
∣du−dv∣≤1 相当于
d
u
>
k
d_u>k
du>k为真时,
d
v
>
k
−
1
d_v>k-1
dv>k−1必须为真;
d
v
>
k
d_v>k
dv>k为真时,
d
u
>
k
−
1
d_u>k-1
du>k−1必须为真;
因为
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]+k−1f[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);
}
}