zoukankan      html  css  js  c++  java
  • 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 旅行

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

  • 相关阅读:
    HDU4628+状态压缩DP
    Javascript 去掉字符串前后空格的五种方法
    Javascript 数组之判断取值和数组取值
    ASP.NET MVC 出现错误 “The view 'XXX' or its master was not found or no view engine support”
    ASP.NET MVC 页面调整并传递参数
    ASP.NET MV3 部署网站 报"Could not load file or assembly ' System.Web.Helpers “ 错的解决方法
    ASP.NET MVC 控制器向View传值的三种方法
    CSharp 如何通过拼接XML调用存储过程来查询数据
    SQLServer : EXEC和sp_executesql的区别
    关于SQLServer2005的学习笔记—异常捕获及处理
  • 原文地址:https://www.cnblogs.com/joyouth/p/5531450.html
Copyright © 2011-2022 走看看