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\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 旅行

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

时间: 2024-08-06 03:18:58

5.26 考试修改+总结的相关文章

5.28 考试修改+总结

今天又是一个悲伤的故事,所有排名比我高的人第一题都A了 而我第一题爆零了 但是开心的事情是:第一题没有说是简单图,所以题解是错的 不管怎么样,将错就错吧 今天下午断网了,所以这时候才写blog 第一题 由于题目中没有给出欧拉图的概念,所以我完全不知道它在说啥,于是就爆零了 然后欧拉图就是存在欧拉回路的简单图,具有以下特点: 1.联通 2.度数都是偶数 显然我们将错就错看题目的话,把欧拉图数目*(n*(n-1)/2+1)就可以得到答案了 然后我们很容易知道度数均为偶数的图的数目是2^((n-1)*

6.15 考试修改+总结

昨天考崩了QAQ 几乎写全了暴力分,然而并没有什么卵用 因为只要A掉一道题就比我分高了,比我分高的也至少A掉了一道题QAQ 感觉到一丝淡淡的忧桑 貌似THUSC最后听讲课的那些人几乎都A了两题 看来我的THUSC果然只是RP好啊 第一题 显然选色数最少的颜色,设颜色数为m 考虑存在某个点的方案数,设这个点到k距离i个点 则方案数为(n-1-i)!/ ((m-i)!*j!*k!……) j,k等是其他颜色的色数 总方案也是非常好算的,这样我们就可以计算每个点对于期望的贡献了 这样做是O(n^2)的

5.27 考试修改+总结

这是一个悲伤的故事 上午写manacher的时候往里面加#号,然后统计有效字符的个数 然后我就开始模拟,一个长度为6的串我都能数错有多少个有效字符 我把2个字符数成了3个!然后暴力就挂掉了5分.. 为什么这几天的暴力总是会挂掉,真是奇怪(看来是最近自己内心不太稳了 (大概是被那个梦吓得吧QAQ) 今天又A了两道题目,感觉天天都是两道正解+挂掉的暴力QAQ 先放题解吧 第一题是之前数位DP坑掉的题目,然后今天尝试着写了写,感觉不是很难 但是并不是用数位DP做的 先考虑加密的情况,我们只需要统计每一

9.26 考试

好久没有写博客了... 言归正传奥 T1就是个送分题... #include <cstdio> #include <cstring> #include <iostream> #define ll long long #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; int n,maxp; ll a[26],b[26],c[26],d[26]; ll ans; int main(){ freopen(&

6.3 考试修改+总结

今天下午考试被FFT和数论题目翔了一脸QAQ 做的是Newscafe杯的题目 第一题 异化多肽 显然构造多项式f 答案是f+f^2+f^3…… 化简一下得1/(1-f) 之后多项式求逆即可 考试的时候推了好久的多项式求逆的式子(感觉自己什么都忘光了 #include<cstdio> #include<cstring> #include<iostream> #include<cstdlib> #include<algorithm> #define

6.11 考试修改+总结

第三题至今没敢写,感觉好恐怖QAQ 今天考得好糟糕 第一题只写了10分的暴力+(k=1)20分的网络流 后来题解告诉我k>1的时候可以分治到k=1,每层分治解决方法是同k=1的 考试的时候没有注意到2^k这个比较神奇的可以分治的性质 而且自己考场上丝薄了,没有发现因为是二分图可以直接跑欧拉回路的性质,而是裸套网络流模型 第二题其实已经接近想出了题解 自己考试的时候成功证明了暴力的复杂度是线性的 但是没有想到如何寻找0-1对,然后就只能暴力用Splay维护1所在的位置了 默默祈祷数据不要太卡我的做

6.10 考试修改+总结+颓废记

昨天晚上得到了非常不爽的消息,zcg要去给高一讲课,而我并不能去 虽然什么事情并不能都顺着我的心意来吧,但是这件事情真是让人越想越不痛快 要知道,我从去年就一直期待着给高一讲课呢 所以今天考试非常不开心,一般这个时候我会选择爆零的 但是想了想觉得爆零太难看,就看了看好像第一题可做 在教学楼里颓废了好久然后吃了点东西,用最后的时间码完了第一题 (反正二.三题我没看出来怎么做,所以暴力也不想写了 然后惊讶的是,只有第一题程序的窝rank1了QAQ 先放题解吧 第一题: 首先我们注意到转置的实质是某个

6.19 考试修改+总结

QAQ 又是一套水题集合 然后忧伤的故事是老师把时间调到了四个小时半 我又因为想要出道题花了半个小时写了一些其他的东西 然后最后没有写完题!QAQ 不然第三题可能多拿点分 上午的时候把所有题目的正解都想了出来 唯一美中不足的是自己想的第三题是O(n^4*300)的是时间复杂度 实际上离散化区间之后只会有n个区间,时间复杂度就是O(n^5)了QAQ 先说题解把 第一题 决战圆锥曲线 显然给定你的那个函数a*x+b*y+c*x*y对于x,y是相对等价的 又因为题目的输入具有随机性,显然可以通过维护线

5.25 考试+修改

论改题只用两分钟的速度QAQ 其实就是换了个数组名字,加上加了一句话 第一题: 首先考虑k=1的情况,考虑构造转移矩阵A ans*(A^0+A^1+……+A^(n-1)) 然后括号里的式子等比数列求和一下 是(A^0-A^n)/(A^0-A^1) 涉及到除法,手动矩阵求逆就可以了 然后这个式子就变成了一个矩阵 我们考虑k>1的情况,发现扩维不过就是又乘了一次这个矩阵 然后把这个矩阵自乘k次即可 (考试的时候犯傻,没有想到k>1的时候直接自乘k次就可以了,下午加了一句话就A了) #include