• 5.26 考试修改+总结


    论写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
    ",(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
    ",(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
    ",(dp[n]%mod+mod)%mod); 
        return 0; 
    }
    

    今天考试比较好的地方:

    1、很快的发现第二题的容斥并成功的推出了式子

    2、第三题的模型很容易就看了出来

    然后就A掉了第二题和第三题

    不太好的地方:

    1、第一题的花式暴力分懒得去写(导致最后连最简单的暴力都挂掉了,关键是没有用心)

    2、第一题想到了维护类似于实边和虚边的东西,但是发现单次可能挂成O(n)就没有往下想

    实际上在分析会发现均摊是O(logn)的

    需要做的题目:SDOI 旅行

    话说天天都坑着一堆题目要做。。

  • 相关阅读:
    LoadRunner测试结果分析
    安装LoadRunner时提示缺少vc2005_sp1_with_atl_fix_redist解决方案
    LoadRunner 11 安装及破解
    android批量文件上传(android批量图片上传)
    nginx优化配置
    redis节点管理-节点的移除
    jQuery获取表格隐藏域与ajax请求数据结合显示详情
    el表达式运算符
    EL表达式中获取list长度(JSTL函数用法)
    SQL之case when then用法(用于分类统计)
  • 原文地址:https://www.cnblogs.com/joyouth/p/5531450.html
Copyright © 2020-2023  润新知