A bzoj3777
先不考虑本质不同,计算总的方案数,问题相当于一个人每步至少跨越k个格子,求走到这n个格子中某个格子后停下来的方案数
我们设这个人一开始在无穷远,第一步走到的位置是0,然后设他走到第i个格子的方案数是
f[i]
f
[
i
]
,不考虑循环对末尾选的限制,有
f[i]=∑i−kj=0f[j]
f
[
i
]
=
∑
j
=
0
i
−
k
f
[
j
]
,转移可以用前缀和优化到
O(1)
O
(
1
)
要考虑循环,计算总的方案数的话,只需枚举第一步走到第
i
i
个格子,这种情况下对应的方案数是
加上本质不同的限制,就是套上一个burnside
他一共有n种置换,第i种是顺时针旋转i个格子,枚举i,要计算旋转i后和原来相同的方案数
我们现在要计算顺时针旋转
i
i
格后和原来相同的方案数
对于一个位置,我们将他旋转若干个
i
i
后到达的位置标成黑色,有一个结论是大小为n的环,顺时针旋转格的置换环有
(i,n)
(
i
,
n
)
个,也就是每隔
(i,n)
(
i
,
n
)
就有一个黑格子(之前某道置换题blog证过就不再证了),因此我们可以理解为这n个格子以
(i,n)
(
i
,
n
)
为循环节
因为一个置换环中被选与不选的状态相同,我们枚举第一个选的置换环
i
i
,令,只要在这
d
d
个格子里面合法,在这n个格子里也合法,而在这个格子中每个置换环只有一个格子,就和上面要算得东西差不多了,方案数是
∑min(d−k,d−i)j=0f[j]
∑
j
=
0
m
i
n
(
d
−
k
,
d
−
i
)
f
[
j
]
(可以发现其实顺时针旋n个不变时的方案数就是上面算的不考虑本质不同的方案数)
这个
j
j
不用枚举,柿子是可以算的
总复杂度 O(nlogn) O ( n l o g n ) (log的复杂度来自gcd)
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int gcd(int a,int b){return !a?b:gcd(b%a,a);}
const int maxn = 210000;
const int mod = 1e9+7;
inline void add(int &a,const int &b){a+=b;if(a>=mod)a-=mod;}
inline void dec(int &a,const int &b){a-=b;if(a<0)a+=mod;}
int pw(int x,int k)
{
int re=1;
for(;k;k>>=1,x=(ll)x*x%mod) if(k&1)
re=(ll)re*x%mod;
return re;
}
int inv(int x){ return pw(x,mod-2); }
int n,K;
int f[maxn],s[maxn],g[maxn];
int main()
{
freopen("refrain.in","r",stdin);
freopen("refrain.out","w",stdout);
scanf("%d%d",&n,&K);
f[0]=1; s[0]=1;
for(int i=1;i<=n;i++)
{
f[i]=i-K<0?0:s[i-K];
s[i]=(s[i-1]+f[i])%mod;
}
g[0]=f[0];
for(int i=1;i<=n;i++) g[i]=(g[i-1]+s[i])%mod;
int ans=0,ansn=0;
for(int i=K;i<=n;i++)
{
if(i==n) ansn=ans;
int d=gcd(i,n); if(d<K) continue;
add(ans,(ll)s[d-K]*K%mod);
add(ans,g[d-K]);
dec(ans,s[d-K]);
}
ansn=(ans-ansn+mod)%mod;
printf("%d\n%lld\n",ansn,(ll)ans*inv(n)%mod);
return 0;
}
B bzoj3778
就是求白点构成的不包含黑点的凸包的最大面积
思考构建凸包的过程,找一个最下最左的点,将其他点极角排序,然后逆时针建进凸包里
我们可以用同样的方法做这道题
枚举最终答案的凸包里最下最左的点,将其他点极角排序,那么凸包上的点一定是按顺序出现的,然后我们就可以做一个dp,设
f[i][j][k]
f
[
i
]
[
j
]
[
k
]
表示dp到第
i
i
个点,当前凸包上最后两个点是,当前凸包的最大面积
输出方案的话记录一下每个状态是从哪个状态转移过来的就行了
复杂度 O(n3(n+m)) O ( n 3 ( n + m ) )
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define db double
#define pb push_back
#define SZ(x) ((int)x.size())
using namespace std;
const int maxn = 105;
const double eps = 1e-7;
int n,m;
struct Point
{
int x,y,i;
friend inline bool operator ==(const Point &a,const Point &b){ return a.x==b.x&&a.y==b.y; }
friend inline Point operator -(const Point &a,const Point &b){return (Point){a.x-b.x,a.y-b.y};}
friend inline int operator *(const Point &a,const Point &b){return a.x*b.y-a.y*b.x;}
db Len() { return sqrt((db)x*x+(db)y*y); }
}p[maxn],pm[maxn],t[maxn]; int tp;
inline bool cmp(const Point &x,const Point &y)
{
return (x-t[0])*(y-x)>0;
}
int In_Line(const Point &x,const Point &y,const Point &q)
{
return (y-x)*(q-y)==0&&fabs((x-q).Len()+(y-q).Len()-(x-y).Len())<eps;
}
int In_Convex(const Point &x,const Point &y,const Point &z,const Point &q)
{
if(x==y) return In_Line(x,z,q);
return (y-x)*(q-y)>=0&&(z-y)*(q-z)>=0&&(x-z)*(q-x)>=0;
}
int ans;
vector<Point>v;
int f[maxn][maxn][maxn],pre[maxn][maxn][maxn];
int main()
{
//freopen("resonance.in","r",stdin);
//freopen("resonance.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d%d",&p[i].x,&p[i].y),p[i].i=i;
for(int i=1;i<=m;i++) scanf("%d%d",&pm[i].x,&pm[i].y);
ans=0;
for(int i=1;i<=n;i++)
{
t[0]=p[i];
tp=0; for(int j=1;j<=n;j++) if(p[j].y>p[i].y||(p[j].y==p[i].y&&p[j].x>p[i].x)) t[++tp]=p[j];
sort(t+1,t+tp+1,cmp);
for(int x=0;x<=tp;x++) for(int y=0;y<=tp;y++) for(int k=0;k<=tp;k++)
f[x][y][k]=pre[x][y][k]=-1;
f[0][0][0]=0;
for(int j=0;j<tp;j++)
{
Point tx=t[j+1];
for(int y=0;y<=j;y++)
{
Point ty=t[y];
int ok=1;
for(int k=1;k<=m&&ok;k++)
ok&=!In_Convex(t[0],ty,tx,pm[k]);
int ad=(ty-t[0])*(tx-ty);
for(int x=0;x<=y;x++) if(f[j][x][y]!=-1)
{
if(f[j+1][y][j+1]<f[j][x][y]+ad&&(ty-t[x])*(tx-ty)>=0&&ok)
f[j+1][y][j+1]=f[j][x][y]+ad,pre[j+1][y][j+1]=x;
if(f[j+1][x][y]<f[j][x][y])
f[j+1][x][y]=f[j][x][y],pre[j+1][x][y]=y;
}
}
}
for(int x=1;x<tp;x++) for(int y=x+1;y<=tp;y++) if(f[tp][x][y]>ans)
{
ans=f[tp][x][y]; v.clear();
v.pb(t[y]); v.pb(t[x]);
int ni=tp,px=x,py=y;
while(ni)
{
int ny=pre[ni][px][py];
if(ny!=py)
{
if(ny!=px) v.pb(t[ny]);
py=ny,swap(px,py);
}
ni--;
}
}
}
printf("%.2lf\n",ans/2.0);
/*printf("%d\n",SZ(v));
for(int i=0;i<SZ(v);i++)
{
printf("%d",v[i].i);
if(i+1!=SZ(v)) putchar(' ');
}putchar('\n');*/
return 0;
}
C bzoj3779
一棵LCT,每次Access,或者makeroot一个点,或者询问一个点子树里所有点到根路径上虚边个数的平均数
先假设没有换根操作
根据LCT的复杂度证明,每次Access,虚改实,实改虚的边数均摊是
O(logn)
O
(
l
o
g
n
)
级别的,我们可以维护一棵LCT,Access时把要修改的边找出来,在dfs序上维护每个点到根路径上的虚边数,每次虚改实,实改虚就是对一个子树区间+1或-1,用线段树很容易做到
O(nlog2n)
O
(
n
l
o
g
2
n
)
加上换根的话,修改子树的时候讨论一下子树的实际范围就好了
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define db double
#define pb push_back
#define mp make_pair
#define SZ(x) ((int)x.size())
using namespace std;
inline void read(int &x)
{
char c; while(!((c=getchar())>='0'&&c<='9'));
x=c-'0';
while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
const int maxn = 210000;
const int maxd = 20;
int n,m;
struct edge{int y,nex;}a[maxn<<1]; int len,fir[maxn];
inline void ins(const int x,const int y){a[++len]=(edge){y,fir[x]};fir[x]=len;}
int root;
int cnt,dfn[maxn],siz[maxn],ff[maxn],df[maxn][maxd],dep[maxn];
void dfs(const int x)
{
for(int i=1;i<maxd;i++) df[x][i]=df[df[x][i-1]][i-1];
siz[x]=1; dfn[x]=++cnt;
for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=ff[x])
{
ff[y]=x; df[y][0]=x;
dep[y]=dep[x]+1;
dfs(y);
siz[x]+=siz[y];
}
}
struct Segment
{
ll seg[maxn<<2],flag[maxn<<2];
void pushdown(const int x,const int l,const int r)
{
if(!flag[x]) return;
int fl=flag[x]; flag[x]=0;
int mid=(l+r)>>1,lc=x<<1,rc=lc|1;
seg[lc]+=(ll)(mid-l+1)*fl; seg[rc]+=(ll)(r-mid)*fl;
flag[lc]+=fl; flag[rc]+=fl;
}
void pushup(const int x){seg[x]=seg[x<<1]+seg[x<<1|1];}
int lx,rx,c;
void upd(const int x,const int l,const int r)
{
if(rx<l||r<lx) return;
if(lx<=l&&r<=rx) { seg[x]+=(ll)(r-l+1)*c;flag[x]+=c;return; }
pushdown(x,l,r);
int mid=(l+r)>>1;
upd(x<<1,l,mid); upd(x<<1|1,mid+1,r);
pushup(x);
}
ll query(const int x,const int l,const int r)
{
if(rx<l||r<lx) return 0;
if(lx<=l&&r<=rx) return seg[x];
pushdown(x,l,r);
int mid=(l+r)>>1;
return query(x<<1,l,mid)+query(x<<1|1,mid+1,r);
}
void Upd(int x,int tc)
{
c=tc?1:-1;
if(dfn[root]>=dfn[x]&&dfn[root]<dfn[x]+siz[x])
{
lx=1,rx=dfn[x]-1; if(lx<=rx) upd(1,1,cnt);
lx=dfn[x]+siz[x],rx=cnt; if(lx<=rx) upd(1,1,cnt);
}
else lx=dfn[x],rx=dfn[x]+siz[x]-1,upd(1,1,cnt);
}
db q(int x)
{
ll ans=0; int Siz;
if(x==root)
{
Siz=n;
lx=1,rx=cnt,ans=query(1,1,cnt);
}
else if(dfn[root]>=dfn[x]&&dfn[root]<dfn[x]+siz[x])
{
int y=root; for(int i=maxd-1;i>=0;i--) if(dep[df[y][i]]>dep[x]) y=df[y][i];
Siz=n-siz[y];
lx=1,rx=dfn[y]-1; ans+=query(1,1,cnt);
lx=dfn[y]+siz[y],rx=cnt; if(lx<=rx) ans+=query(1,1,cnt);
}
else
{
Siz=siz[x];
lx=dfn[x],rx=dfn[x]+siz[x]-1,ans=query(1,1,cnt);
}
return (db)ans/Siz;
}
}seg;
struct Link_Cut_Tree
{
int son[maxn][2],fa[maxn],rev[maxn];
void pushdown(int x)
{
if(rev[x])
{
rev[x]=0;
rev[son[x][0]]^=1,rev[son[x][1]]^=1;
swap(son[x][0],son[x][1]);
}
}
bool isrt(const int x){ return son[fa[x]][0]!=x&&son[fa[x]][1]!=x; }
void rot(int x)
{
int y=fa[x],z=fa[y];
if(!isrt(y)) son[z][son[z][1]==y]=x;
fa[x]=z;
int l=son[y][1]==x;
fa[son[y][l]=son[x][!l]]=y;
fa[son[x][!l]=y]=x;
}
int t[maxn],tp;
void splay(int x)
{
int tx=x; while(!isrt(tx)) t[++tp]=tx,tx=fa[tx]; t[++tp]=tx;
while(tp) pushdown(t[tp--]);
for(;!isrt(x);rot(x))
{
int y=fa[x],z=fa[y];
if(!isrt(y)) rot(((son[y][1]==x)^(son[z][1]==y))?x:y);
}
}
int go(int x,int dir)
{
pushdown(x);
while(son[x][dir])
{
x=son[x][dir];
pushdown(x);
}
return x;
}
void Access(int x)
{
int y=0;
for(;x;y=x,x=fa[x])
{
splay(x);
int ty=go(son[x][1],0);
if(ty) seg.Upd(dfn[x]>dfn[ty]?x:ty,1);
int nty=go(y,0);
if(nty) seg.Upd(dfn[x]>dfn[nty]?x:nty,0);
son[x][1]=y;
}
}
void makeroot(int x)
{
Access(x); splay(x); rev[x]^=1;
root=x;
}
void build(const int x)
{
for(int i=2;i<=n;i++) fa[i]=ff[i],seg.Upd(i,1);
}
}LCT;
char str[110];
int main()
{
//freopen("recompile.in","r",stdin);
//freopen("recompile.out","w",stdout);
read(n); read(m);
for(int i=1;i<n;i++)
{
int x,y; read(x);read(y);
ins(x,y); ins(y,x);
}
dep[1]=1; dfs(root=1);
LCT.build(1);
for(int i=1;i<=m;i++)
{
scanf("%s",str); int x;read(x);
if(str[2]=='L') LCT.Access(x);
else if(str[2]=='C') LCT.makeroot(x);
else printf("%.10lf\n",seg.q(x)+1.0);
}
return 0;
}