初三上学期比赛笔记(Part1)

博主记录了自己参与初三数学竞赛的过程,分享了关于树状结构问题的解题思路,探讨了如何处理链的最优解以及在解决字符串问题时采用的多项式和图论方法。此外,还提到了差分约束系统的应用和如何优化回文串循环移位问题的解决方案。博主强调了解题策略的重要性,包括选择题目的智慧、提高代码能力和快速解决问题的技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2019-9-2
哇,我初三啦!
2019-9-7
感觉我打了场假赛,A,B很假,打的时候人也很假
C挺有意思的
给你一颗树,每个点的权值为0或1
每次你可以选定一个点u
对于满足 x 到 v 的路径上所有碎片的状态值与 x 的状态值相同 的那些点v 状态值都取反(0变1,1变0)
求最少操作次数,使的最后每个点的状态值相等
n ⩽ 5 ∗ 1 0 5 n\leqslant 5*10^5 n5105
考虑树的直径d,每次操作最多减2
我们要让它为0,至少需要 ⌊ d + 1 2 ⌋ \lfloor \frac{d+1}{2} \rfloor 2d+1
怎么构造!
将链的中点作为根即可!
2019-9-8
真,被打爆了!
怎么A这么傻逼自己都想不出来
一颗n点的树上给你m条链
每个点度数不超过10
问最多安排多少条链
使得链之间不会相交(对于边而言)
n ⩽ 500 , m ⩽ n ∗ ( n − 1 ) 2 n\leqslant 500,m\leqslant \frac{n*(n-1)}{2} n500,m2n(n1)
多组询问
T ⩽ 100 , ∑ n ⩽ 18000 T\leqslant 100,\sum n\leqslant 18000 T100,n18000
这道题,可以很明显看出一个性质
肯定存在一个最优解,对于每棵子树都是最优解
考虑维护 f i f_i fi表示子树i的最优解
h i h_i hi表示i为最优解时的可用节点
然后随便搞搞就好了(利用度数为10进行匹配)
2019-9-10
又打了一场假赛
三题挂了两题
好东西
2019-9-11
自闭了,C真的做不出啊
你有两个长度都为N的01字符串A和B。A中的1的个数和B中的1的个数相等。
题意
你想通过以下方式把A变成B:

  1. 设a1,a2,…,ak是所有A中的1的下标。
  2. 设b1,b2,…,bk是所有B中的1的下标。
  3. 将a1,a2,…,ak随机排序,每种排列出现的概率都是1k!,这一步会产生k!种不同的结果。
  4. 将b1,b2,…,bk随机排序,每种排列出现的概率都是1k!,这一步会产生k!种不同的结果。
  5. 对于所有1≤i≤k,依次交换Aai和Abi。

记P为这样搞完之后A和B变得相等的概率。显然,P×(k!)2是个整数,你需要计算这个整数对998244353取模后的值。
A,B为0,1串,至少一个1
长度小于等于10000
解法
把该题目当成图论来理解
swap(a,b)在a,b间连边
设x个公共1,y个“独”1
那么对于一种方案(不管能不能把A变B)会形成y条链,每条链的端点分别是两个y之一
那,如何判断一个方案能不能把A变B
问题的关键在于它能否合理地处理链
你会发现,每条链的交换顺序只能是唯一的,所以合法必定满足这个顺序
所以我们考虑求链的方案数
设i个公共1,j个非公共, d p i , j dp_i,j dpi,j表示此时链的方案数,考虑编号
d p i , j = d p i − 1 , j ∗ i ∗ j + d p i , j − 1 ∗ j ∗ j dp_{i,j}=dp_{i-1,j}*i*j+dp_{i,j-1}*j*j dpi,j=dpi1,jij+dpi,j1jj
最后考虑环的时候随便乘上一些系数就好了

然而,你觉得复杂度就是这样的吗,不,你错了!
要知道,世界上有一种东西,叫做多项式!
多项式吼啊!但我们要怎么用它!
f i , j = d p i , j i ! j ! f_{i,j}=\frac{dp_{i,j}}{i!j!} fi,j=i!j!dpi,j
然后 f i , j = f i − 1 , j ∗ j + f i , j − 1 ∗ j f_{i,j}=f_{i-1,j}*j+f_{i,j-1}*j fi,j=fi1,jj+fi,j1j
每到达一个新点,乘上它的纵坐标
这个可以表示成若干个形如 1 1 − i x \frac{1}{1-ix} 1ix1的乘积的某项系数
于是分治FFT+多项式求逆即可
O ( n l o g 2 n 2 ) O(nlog_2n^2) O(nlog2n2)

cao,不是说多项式的复杂度都是一个log的吗,你在骗我
当然,我们有一个log的优秀做法
大概是推出另外一个dp式子,然后发现它可以表示成一个多项式的若干次幂
于是,当然不是分治啦,exp和ln就行了

好像有一道agc神奇的题目,记录一下
对于一个串S
翻转它的一个区间[l,r]得到一个新串
问可以得到多少个不同的区间
答案是有多少对 ( i , j ) (i,j) (i,j)
满足 S i ! = S j S_i!=S_j Si!=Sj
因为新串(若不等于S)所确定的旋转中心只有一个
2019-9-12
复习了一下差分约束
摘自yww大爷的博客
如果只有 a i ≤ a j + d ai≤aj+d aiaj+d 的约束,就可以直接上差分约束。
如果有 a i + a j ≤ d ai+aj≤d ai+ajd 的约束,考虑整张图黑白染色,使得同色点之间只有差的约束,异色点之间只有和的约束,然后把白色的点的值取反,就可以跑差分约束了。
白色的点值取反,不影响表示它们之间的差
2019-9-14
T2很好玩
给定一个整数N,请你求出有多少字符集为1到K之间整数的字符串,使得该字符串可以由一个长度为N的回文串循环移位后得到。
考虑一下一个回文串循环移位得到的字符串
这会有个最小周期
采用减去因数的方式容斥
如果其中有回文串
必然该周期是偶数,且只有一个
因为循环t次与周期d-t次是reverse的关系
且周期内没有两个相同的串
做完撒花
2019-9-15
看看T2
题意
有一棵N个点,顶点标号为1到N的树。N−1条边中的第i条边连接顶点ai和bi。
每条边在初始时被染成蓝色。高桥 将进行N−1次操作,来把这棵蓝色的树变成红色的树。

  • 选一条仅包含蓝色边的简单路径,并删除这些边中的一条。
  • 然后在路径的两个端点中间连一条红色的边。
    他的目标是,对于每一个i,都有一条红色的边连接ci和di。
    现在请你判断是否可能达成他的目标。
    n ⩽ 1 0 5 n\leqslant 10^5 n105
    考虑最后加入的红边
    一定在蓝树上也是一条边
    于是从后往前考虑这些红边
    选一条蓝红交集
    然后将这条边的两端顶点合并即可
    T3
    当你发现dp的状态有点多的时候
    考虑是不是有一些冗余状态
    启发式合并欢迎您

2019-9-17
状态不是很好了,不过加油,好好总结一下!
T2挺神仙的
题意:n堆糖果
每次你可以进行一下两个操作中的一个P:
1.删除个数最大(有多个选一个)的那一堆
2.给所有堆的糖果个数都减1
n ⩽ 1 0 5 , a i ⩽ 1 0 9 n\leqslant 10^5,a_i\leqslant 10^9 n105,ai109
怎么做呢?
图形法+找规律,算是长见识了
请忽略上面的日文
如果将 a i a_i ai从大到小排序
每次的操作相当于删除最左边的列或删除最下面一行
b i , j b_{i,j} bi,j表示删除 [ 1 , i ) [1,i) [1,i) [ 1 , j ) [1,j) [1,j)行时先后手必胜
0后手,1先手
那么边界的值可以很好的算出来
且对角线 ( x , y ) − ( x + 1 , y + 1 ) (x,y)-(x+1,y+1) (x,y)(x+1,y+1)上的b值一样
是不是特别好
于是弄到(1,1)所在的对角线末尾(也就是边界上)的b值即可
O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)

T3也是蛮有意思的
题意:给你两颗树(点编号均为1~n)A,B和上面的点X,Y
交替移动X,Y或不移动(操作次序如上)
X想要尽量完相遇,Y想要尽量早相遇
输出答案(相遇的时间,操作一次算一个时间单位)
无限时间则输出-1
解法:遇到这种题,不妨想一下如何判-1
一种情况是存在X在A上相连的边在B上的距离不小于3
以此为突破点,我们继续考虑
那么,判掉这种情况后,我们不妨考虑答案操作序列的情形
肯定是Y抓到X
如果X最后选择移动,选择待在原地肯定不会差
这样又给了我们一个启发
如果我们对于一个点u,考虑X是否可以通过某种方式到达,且到达后不会被Y抓到(是对Y随后进行的单次操作而言)
很好!如果能进行无限次操作,意味着可以到达一个点u,在A上有"边权"大于等于3的边与它相邻!
如果将B树以Y为根
那么Y及其相邻的点就像一个大蜘蛛
它会如何抓到X
显然,抓到前X只能移动1,2的距离(在B树上)
那么归纳假设当前X在它子树内(不包括该点Y)
X移动到它的父亲,儿子
Y往它的方向走即可(还有可能X傻逼地撞上Y)
X移动到它的父亲的父亲
也许Y往上一走,也许仍然选择往它的儿子走
X移动到它的兄弟,儿子的儿子
Y往下移动,赶尽杀绝
这样,我们形象地描述Y对X的追赶环节

好了,我们发现上面Y对X的追赶建立在X一直没走边权为大于等于3的边
那么我们将A中边权大于等于3的边删掉
与Y仍然联通的点形成了一棵新树M
考虑M中的每个u能不能到达
根据上面Y对X的追赶不难得出
如果一个点u想要被到达,不要走回头路,也就是X应该在A上直接走X到u的最短路
如果一个点u可以被到达,那么它在A上的所有祖先也可以被到达
fine!
关于u可以被到达的充分必要条件,看代码,结合Y对X的追赶不难理解
若可以到达M的边界且M的大小不等于n,那么就可以走无限步

好了,我们成功判断了可不可以走无穷步
那,要是有限步呢…
根据刚才的讨论,有限步的话
操作序列的最后一项不妨选等待,换句话说,“等死”
那那些点可以选择等待啊?没错,就是那些u
设u到Y的距离为dis2,到X的距离为dis1
d i s < d i s 2 dis<dis2 dis<dis2
那么对于任何一种到u的操作路径
由于当中的路径(长度为len)不会被抓
所以在B上它会到dep为len的位置,且 l e n < d i s 2 len<dis2 len<dis2
然后此时“等死”,Y从dep走到了dis2,时间为dis22
所以我们刚才论证了什么?任意一种这样的操作路径如果存在,那么时间会为dis2
2
而这种操作路径肯定存在,对应题设,取X在A上到u的最短路径即可
完结撒花!咋感觉我废话一堆
对于这种题目,应该加上一份代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,X,Y;
#define Maxn 200010
int Ans=0;
struct Graph{
	int head[Maxn],v[Maxn<<1],nxt[Maxn<<1],tot=0;
	int fa[Maxn][18],dep[Maxn];
	void add_edge(int s,int e){
		tot++;v[tot]=e;nxt[tot]=head[s];head[s]=tot;
		tot++;v[tot]=s;nxt[tot]=head[e];head[e]=tot;
	}
	void dfs(int u,int f){
		fa[u][0]=f;
		for(int i=1;i<=17;++i)fa[u][i]=fa[fa[u][i-1]][i-1];
		for(int i=head[u];i;i=nxt[i])
		    if(v[i]^f){
		    	dep[v[i]]=dep[u]+1;
		    	dfs(v[i],u);
			}
	}
	void go_up(int &k,int d){
		for(int i=17;i>=0;--i)
		    if(d&(1<<i))k=fa[k][i];
	}
	int LCA(int a,int b){
	    if(dep[a]>dep[b])go_up(a,dep[a]-dep[b]);
		if(dep[a]<dep[b])go_up(b,dep[b]-dep[a]);
		if(a==b)return a;
		for(int i=17;i>=0;--i)
		    if(fa[a][i]!=fa[b][i]){
		    	a=fa[a][i];
		    	b=fa[b][i];
			}	
		return fa[a][0];
	}
	int Dis(int a,int b){
		int lca=LCA(a,b);
		return dep[a]+dep[b]-2*dep[lca];
	}
}A,B;

void solve(int u,int f){
	if(A.dep[u]>=B.dep[u])return;
	if(Ans!=-1)Ans=max(Ans,B.dep[u]*2);
	for(int i=A.head[u];i;i=A.nxt[i])
	    if(A.v[i]^f){
	    	solve(A.v[i],u);
	    	if(B.Dis(u,A.v[i])>=3)Ans=-1;
		}
}

inline void rd(int &x){
	x=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
}

int main(){
	rd(n);rd(X);rd(Y);
	int s,e;
	for(register int i=1;i<n;++i){
		rd(s);rd(e);
		A.add_edge(s,e);
	}
	for(register int i=1;i<n;++i){
		rd(s);rd(e);
		B.add_edge(s,e);
	}
	A.dfs(X,0);
	B.dfs(Y,0);
	solve(X,0);
	printf("%d\n",Ans);
	return 0;
}

2019-9-18
复习一下笛卡尔树

void Build_Tree(){
    for(int i=1;i<=n;++i){
        last=0;
        while(top){
            if(h[st[top]]>h[i]){
                if(ch[st[top]][1])ch[i][0]=ch[st[top]][1];
                ch[st[top]][1]=i;
                break;
            }
            last=st[top];
            top--;
        }
        if(!top&&last)ch[i][0]=last;
        st[++top]=i;
    }
}

int query(int root,int l,int r)
{
	while(root<l||root>r)
		root= root<l? rs[root]:ls[root];
	return root;
}//h[root]为区间最大值

感觉今天的比赛不难
T1是三道题中最难的,而自己开场比赛选择去肝它
肝是肝出来了,但是花了三个小时
比较感人
然后1h中花了30min想出T3,没时间写了
T2最后也没写,其实跟T3一样简单
总之,这场比赛反映了我几个问题:
1.不会挑题,当前题感太差。例如T3这道题目,照理来说30min内应该想出来。现在最好是开场把每道题目都看一遍,每道题目先花一个10min想一想(这期间有没有思路很关键)。总之,还是多做题,找对题感,提高自己的下限(我想题目的下限实在是太低了),能开场秒掉套路题和一些思维难度低的思维题(最好想出这些题目不要超过30min,这是我要做的基本下限,也是这个学期的集训目标)。如果发现3道题都不会,最好每道先打一下暴力吧,然后一道道地肝下去。
2.代码能力有待提高今天码T1竟然花了我100min,其实,加上对拍,最好要在1h内完成这一切
3.Bless all
三道题目的做法都讲一下吧
A:这种题目,做法没必要讲了,主要记住一个套路,(x,y)的点可以考虑将x,y坐标当成二分图的两个部分,然后进行连边。
B:推出dp式子,然后FFT,挺套路的
C.其实最好写一下自己的做题思路
考虑一下一条S-T的路径
肯定有个xmin和xmax
然后只要在x在xmin和xmax中更新即可得到答案
于是一些点的xmin和xmax肯定有交集
把它们放在一起更新肯定不错
优化复杂度
所以这启示我们去分治
考虑当前xmin和xmax是否包含mid
于是更新一下
如果不包含
可以把它分成两个子问题
完美
O ( H W l o g 2 H W l o g 2 H + Q W l o g 2 H ) O(HWlog_2{HW}log_2H+QWlog_2H) O(HWlog2HWlog2H+QWlog2H)
2019-9-20
今天的题目还是很神奇的,其实蛮水的!
主要是我要加快推题的速度!打代码的速度!
set的操作考虑势能分析
φ ( a b ) = φ ( a ) φ ( b ) ( a , b ) φ ( ( a , b ) ) \varphi(ab)=\frac{\varphi(a)\varphi(b)(a,b)}{\varphi((a,b))} φ(ab)=φ((a,b))φ(a)φ(b)(a,b)
2019-9-21
欢乐场
欢乐找规律
欢乐推结论
欢乐做神题(反向思考)

2019-9-23
糟糕场
t1还是值得总结一下的
选K个区间,有顺序,使得每个区间的数加1(原本为0),然后所有数不超过T的方案数。
n , K , T ⩽ 40 n,K,T\leqslant 40 n,K,T40
考虑从括号括号匹配的思路去考虑
但是,你会发现无序的很难统计,无需转成有序更难统计,因为需要乘上组合数
于是,把左右括号先放在K个括号中更好
多出来的左括号也要放
t3的话,自己应该能做出来,但是考场的时候为什么不敢朝这个方向想,自闭了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值