标签:
论写5K+的代码在只有样例的条件下都可以调对
由此可见,勇气才是成功的关键
先放题解吧
第一题上午写的暴力不小心忘记题目换根之后还会染色了
然后就挂成了5分QAQ
有很大的部分分是SDOI染色,还有一部分是旅行
但是考试犯懒没有写
很容易发现任何一种颜色在树上都是连续的一段
那么我们不妨这么定义,如果一条边两端颜色不相同,我们定义为虚边,会对子树每个答案产生+1的贡献
如果两端颜色相同,我们定义为实边,不会产生贡献
不难发现,这样定义后的实边和虚边的性质和LCT的定义是一样的
我们考虑使用LCT来维护,每次修改到根的路径就是一个Access
每次Access会使一些实边变成虚边,一些虚边变成实边
很容易在Access的过程中确定这些边,可以发现这些边的数量是等价于LCT复杂度的
也就是单次均摊O(logn),我们对于每次边的变化在对DFS序维护一颗线段树进行子树加减和子树求和就可以了
注意到这里LCT的儿子并不是原树中的儿子,所以我们要对于每个点维护他一直向左走到达的点L
这样修改的目标节点是LCT中的儿子的L,但是由于有换根操作,我们会有rev标记,所以我们还要额外维护一直向右走的点R
我们考虑换根操作,LCT显然可以直接换根
对于我们的线段树,我们可以使用类似于BZOJ 遥远的国度 的讨论方法,可以在不换根的情况下讨论出换根的信息
具体讨论方法就不在多说了,注意如果是菊花树,当根在当前点的子树内的时候,暴力找会挂掉
所以我们对于每个点开vector记录孩子,然后把孩子按DFS序排序,每次二分即可
总时间复杂度O(nlog^2n)
然后ftp挂掉了,没有数据的情况下我居然调了调就A了!撒花~~
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<vector>
using namespace std;
typedef long long LL;
const int maxn=100010;
int n,m,u,v,rt;
LL ans,sz;
char s[maxn];
int h[maxn],cnt=0;
struct edge{
int to,next;
}G[maxn<<1];
int pos[maxn],ed[maxn],tot=0;
vector<int>V[maxn];
LL S[maxn<<2];
int Add[maxn<<2];
int siz[maxn];
bool cmp(const int &x,const int &y){
return pos[x]<pos[y];
}
void add(int x,int y){
++cnt;G[cnt].to=y;G[cnt].next=h[x];h[x]=cnt;
}
void push_down(int o,int L,int mid,int R){
int cl=(o<<1),cr=(o<<1|1);
int now=Add[o];Add[o]=0;
S[cl]+=now*(mid-L+1);Add[cl]+=now;
S[cr]+=now*(R-mid);Add[cr]+=now;
}
void UPD(int o,int L,int R,int x,int y,int v){
if(L>=x&&R<=y){
S[o]=S[o]+v*(R-L+1);
Add[o]+=v;
return;
}
int mid=(L+R)>>1;
if(Add[o]!=0)push_down(o,L,mid,R);
if(y<=mid)UPD(o<<1,L,mid,x,y,v);
else if(x>mid)UPD(o<<1|1,mid+1,R,x,y,v);
else {UPD(o<<1,L,mid,x,y,v);UPD(o<<1|1,mid+1,R,x,y,v);}
S[o]=S[o<<1]+S[o<<1|1];
}
LL ask(int o,int L,int R,int x,int y){
if(L>=x&&R<=y)return S[o];
int mid=(L+R)>>1;
if(Add[o]!=0)push_down(o,L,mid,R);
if(y<=mid)return ask(o<<1,L,mid,x,y);
else if(x>mid)return ask(o<<1|1,mid+1,R,x,y);
else return ask(o<<1,L,mid,x,y)+ask(o<<1|1,mid+1,R,x,y);
}
int find_pos(int u,int v){
int L=0,R=V[u].size()-1;
while(L<R){
int mid=L+((R-L+1)>>1);
int now=V[u][mid];
if(pos[v]>=pos[now])L=mid;
else R=mid-1;
}return V[u][L];
}
void Get_modify(int u,int val){
if(u==rt)UPD(1,1,n,1,n,val);
else if(pos[rt]<=ed[u]&&pos[rt]>=pos[u]){
int v=find_pos(u,rt);
UPD(1,1,n,1,n,val);
UPD(1,1,n,pos[v],ed[v],-val);
}else UPD(1,1,n,pos[u],ed[u],val);
}
LL Get_ans(int u){
if(u==rt)return ask(1,1,n,1,n);
else if(pos[rt]<=ed[u]&&pos[rt]>=pos[u]){
int v=find_pos(u,rt);
LL A=ask(1,1,n,1,n);
LL B=ask(1,1,n,pos[v],ed[v]);
return A-B;
}else return ask(1,1,n,pos[u],ed[u]);
}
int Get_sz(int u){
if(u==rt)return n;
else if(pos[rt]<=ed[u]&&pos[rt]>=pos[u]){
int v=find_pos(u,rt);
return n-siz[v];
}else return siz[u];
}
struct link_cut_tree{
int fa[maxn],c[maxn][2];
int rev[maxn],L[maxn],R[maxn];
#define fa(u) fa[u]
#define c(u,i) c[u][i]
#define rev(i) rev[i]
#define L(i) L[i]
bool isroot(int u){return c(fa(u),0)!=u&&c(fa(u),1)!=u;}
void pre(int p){if(!isroot(p))pre(fa(p));down(p);}
void flip(int u){swap(c(u,0),c(u,1));swap(L[u],R[u]);rev(u)^=1;}
void init(){for(int i=1;i<=n;++i)L[i]=i,R[i]=i;}
void down(int u){
if(rev(u)){
if(c(u,0))flip(c(u,0));
if(c(u,1))flip(c(u,1));
rev(u)=0;
}return;
}
void up(int u){
if(c(u,0))L[u]=L[c(u,0)];
else L[u]=u;
if(c(u,1))R[u]=R[c(u,1)];
else R[u]=u;
}
void rotate(int p,int x){
int mark= p==c(x,1),y=c(p,mark^1),z=fa(x);
if(c(z,0)==x)c(z,0)=p;
if(c(z,1)==x)c(z,1)=p;
if(y)fa(y)=x;
fa(p)=z;c(p,mark^1)=x;fa(x)=p;c(x,mark)=y;
up(x);
}
void Splay(int p){
pre(p);
while(!isroot(p)){
int x=fa(p),y=fa(x);
if(isroot(x))rotate(p,x);
else if(p==c(x,0)^x==c(y,0))rotate(p,x),rotate(p,y);
else rotate(x,y),rotate(p,x);
}up(p);return;
}
void Access(int u){
for(int v=0;u;u=fa(u)){
Splay(u);
fa(v)=u;
if(c(u,1))Get_modify(L[c(u,1)],1);
c(u,1)=v;
if(v)Get_modify(L[v],-1);
v=u;
}return;
}
void make_root(int u){Access(u);Splay(u);flip(u);}
}LCT;
void read(int &num){
num=0;char ch=getchar();
while(ch<‘!‘)ch=getchar();
while(ch>=‘0‘&&ch<=‘9‘)num=num*10+ch-‘0‘,ch=getchar();
}
void Get_DFS(int u,int f){
pos[u]=++tot;siz[u]=1;
LCT.fa[u]=f;
for(int i=h[u];i;i=G[i].next){
int v=G[i].to;
if(v==f)continue;
V[u].push_back(v);
Get_DFS(v,u);
siz[u]+=siz[v];
}ed[u]=tot;
}
int main(){
read(n);read(m);
for(int i=1;i<n;++i){
read(u);read(v);
add(u,v);add(v,u);
}Get_DFS(1,0);rt=1;
LCT.init();
for(int i=1;i<=n;++i)sort(V[i].begin(),V[i].end(),cmp);
for(int i=1;i<=n;++i)UPD(1,1,n,pos[i],ed[i],1);
for(int i=1;i<=m;++i){
scanf("%s",s+1);
read(u);
if(s[3]==‘Q‘){
ans=Get_ans(u);
sz=Get_sz(u);
printf("%.10lf\n",(double)(ans)/(double)(sz));
}else if(s[3]==‘L‘){
LCT.Access(u);
}else{
//rt=u;
LCT.make_root(u);
rt=u;
}
}return 0;
}
第二题很容易发现用组合数算出取哪k个,然后这k个数组成一个置换的方案是(k-1)!,剩余的数就是一个错排函数f
那么就是C(n,k)*(k-1)!*f(n-k)
但是我们发现对于同一个序列,因为它可能有多个k个数组成一个置换的情况,所以我们会算重
然后容斥一下就好了,容斥的式子也是很容易推导的
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=500010;
const int mod=1e9+7;
int n;
LL jc[maxn],inv[maxn];
LL f[maxn],g[maxn];
LL ans=0;
LL pow_mod(LL v,int p){
LL tmp=1;
while(p){
if(p&1)tmp=tmp*v%mod;
v=v*v%mod;p>>=1;
}return tmp;
}
LL C(int n,int m){
return jc[n]*inv[m]%mod*inv[n-m]%mod;
}
int main(){
scanf("%d",&n);
jc[0]=1;
for(int i=1;i<=n;++i)jc[i]=jc[i-1]*i%mod;
inv[n]=pow_mod(jc[n],mod-2);
for(int i=n-1;i>=0;--i)inv[i]=inv[i+1]*(i+1)%mod;
f[0]=1;f[1]=0;
for(int i=2;i<=n;++i)f[i]=(f[i-1]+f[i-2])*(i-1)%mod;
g[0]=1;g[1]=0;g[2]=1;
for(int i=3;i<=n;++i)g[i]=g[i-1]*(i-1)%mod;
for(int k=2;k<=n;++k){
int cnt=0;
LL tmp=1;
for(int j=k;j<=n;j+=k){
cnt++;
tmp=tmp*C(n-j+k,k)%mod;
tmp=tmp*g[k]%mod;
if(cnt&1)ans=ans+tmp*inv[cnt]%mod*f[n-j]%mod;
else ans=ans-tmp*inv[cnt]%mod*f[n-j]%mod;
if(ans<0)ans+=mod;
if(ans>=mod)ans-=mod;
}
}printf("%lld\n",(ans%mod+mod)%mod);
return 0;
}
第三题是个非常丝薄的题目
首先我们搞出这个序列,不难发现题目要求求极长最长上升子序列的个数
由于n<=1000,直接n^2暴力DP就可以了
考试的时候一直在想这个题目是可以O(n)做的啊,后来才发现读入复杂度都是O(n^2)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int maxn=1010;
const int oo=0x7fffffff;
const int mod=1e9+7;
int n,m,u,v;
int deg[maxn];
int a[maxn];
int Num[maxn];
int dp[maxn];
bool vis[maxn][maxn];
void read(int &num){
num=0;char ch=getchar();
while(ch<‘!‘)ch=getchar();
while(ch>=‘0‘&&ch<=‘9‘)num=num*10+ch-‘0‘,ch=getchar();
}
int main(){
read(n);read(m);
for(int i=1;i<=m;++i){
read(u);read(v);
if(u>v)swap(u,v);
deg[u]++;
}
for(int i=1;i<=n;++i)Num[i]=i;
for(int i=1;i<=n;++i){
int now=deg[i]+1;
a[i]=Num[now];
for(int j=now;j<=n;++j)Num[j]=Num[j+1];
}
n++;a[0]=0;a[n]=n;
for(int i=0;i<=n;++i){
int mn=oo;
for(int j=i+1;j<=n;++j){
if(a[j]>a[i]){
if(mn>a[j])vis[i][j]=true;
mn=min(mn,a[j]);
}
}
}
dp[0]=1;
for(int i=1;i<=n;++i){
for(int j=0;j<i;++j){
if(vis[j][i]){
dp[i]+=dp[j];
if(dp[i]>=mod)dp[i]-=mod;
}
}
}printf("%d\n",(dp[n]%mod+mod)%mod);
return 0;
}
今天考试比较好的地方:
1、很快的发现第二题的容斥并成功的推出了式子
2、第三题的模型很容易就看了出来
然后就A掉了第二题和第三题
不太好的地方:
1、第一题的花式暴力分懒得去写(导致最后连最简单的暴力都挂掉了,关键是没有用心)
2、第一题想到了维护类似于实边和虚边的东西,但是发现单次可能挂成O(n)就没有往下想
实际上在分析会发现均摊是O(logn)的
需要做的题目:SDOI 旅行
话说天天都坑着一堆题目要做。。
标签:
原文地址:http://www.cnblogs.com/joyouth/p/5531450.html