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

博主分享了参加初中竞赛的算法心得,涉及链的性质、子集和的处理、大数据结构、期望计算、矩阵乘法、最短路建模等多方面内容,通过实例解析了各类问题的解题思路和技巧。

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

2019-9-24
A:
挺好玩的,做不出来也有一点无奈
如果有个点u在某个时刻deg为3,v1,v2,v3
那么答案一定只能在u,v1,v2,v3中
于是维护这四个点的答案就好
当所有点度数小于等于2的时候,随便讨论一下就好了
B:子集和
一个好的突破点是:给出的子集和是若干个形如 1 + x y 1+x^y 1+xy的乘积
把0去掉
那么对于当前多项式(然后这个多项式的指数可以是负数…)
的次小指数-最小指数就是当前绝对值最小的y的绝对值
你发现
1 + x y = x y ( 1 + x − y ) 1+x^y=x^y(1+x^{-y}) 1+xy=xy(1+xy)
所以这些乘起来只有一个位移的区别
excellent!
需要乘起来的最大指数和原多项式的最大指数一样
这样的情况下
绝对值大的定好尽量为负
这样的情况下,简单的dp+贪心即可

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll inf=1e18;
int n;
#define Maxn 10010
ll all,maxv,to;
int num0;int S;
ll s[Maxn],p[Maxn];
struct P{
	ll rat,at;
	bool operator <(const P &z)const{return at>z.at;}
}A[Maxn],B[Maxn],C[Maxn],D[Maxn];
int len;
ll val[Maxn];int cnt;
bool ok[Maxn];
bool dp[62][Maxn];

void Divide(ll y){
	val[++cnt]=y;
	int L=0,at1=1,at2=1,at3=0;
	while(at1<=len||at2<=L){
		ll tmp=-inf;
		if(at1<=len)tmp=max(tmp,A[at1].at);
		if(at2<=L)tmp=max(tmp,B[at2].at);
		ll s=0;
		if(at1<=len&&tmp==A[at1].at)s+=A[at1++].rat;
		if(at2<=L&&tmp==B[at2].at)s+=B[at2++].rat;
		if(!s)continue;
		C[++at3]=(P){s,tmp};
		B[++L]=(P){-s,tmp-y};
	}
	len=at3;
	for(register int i=1;i<=len;++i)A[i]=C[i];
}
void Mul(ll y){
	for(int i=1;i<=len;++i){
		B[i]=A[i];
		C[i]=(P){A[i].rat,A[i].at+y};
	}
	int pre=len;
	int hd1=1,hd2=1;
	len=0;
	while(hd1<=pre&&hd2<=pre){
		ll tmp=max(B[hd1].at,C[hd2].at),s=0;
		if(B[hd1].at==tmp)s+=B[hd1++].rat;
		if(C[hd2].at==tmp)s+=C[hd2++].rat;
		A[++len]=(P){s,tmp};
	}
	for(int i=hd1;i<=pre;++i)A[++len]=B[i];
	for(int i=hd2;i<=pre;++i)A[++len]=C[i];
}
ll ans[Maxn];

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

int main(){
	int T;
	rd(T);
	for(register int tt=1;tt<=T;++tt){
		rd(n);
		all=0;len=n;
		num0=0;cnt=0;
		maxv=-inf;to;
		for(register int i=1;i<=n;++i)rd(A[i].at);
		for(register int i=1;i<=n;++i){
			rd(A[i].rat);
			D[i]=A[i];
			all+=A[i].rat;
			if(A[i].at>=maxv){
				maxv=A[i].at;
				to=A[i].rat;
			}
		}
		sort(A+1,A+n+1);
		sort(D+1,D+n+1);
		while(to%2==0){to/=2;num0++;}
		S=0;
		for(int i=1;i<=n;++i)A[i].rat/=(1ll<<num0);
	    while(all%2==0){
	    	all/=2;
	    	S++;
		}
		S-=num0;
		cnt=0;
		for(register int i=1;i<=S;++i)
			Divide(A[len-1].at-A[len].at);
		sort(val+1,val+S+1);
		len=1;
		memset(ok,false,sizeof(bool)*(S+1));
		A[1].at=0;A[1].rat=1;
		ll sum=0;
		for(register int i=1;i<=cnt;++i)sum+=val[i];
		for(register int i=1;i<=n;++i)D[i].at+=sum-maxv;
		int zz;
		for(register int i=1;i<=n;++i){
		    if(!D[i].at)dp[0][i]=true;
		    else dp[0][i]=false;
		    if(D[i].at==maxv)zz=i;
		}
		int At;
		if(dp[0][zz])At=0;
		else{
		    for(register int i=1;i<=S;++i){
	            int haha=1;
				for(register int j=1;j<=n;++j){
					dp[i][j]=dp[i-1][j];
					while(haha<=n&&D[j].at-val[i]<D[haha].at)haha++;
					if(haha<=n&&D[haha].at==D[j].at-val[i])dp[i][j]|=dp[i-1][haha];
				}
				if(dp[i][zz]){
					At=i;
					break;
				}
	        }
		}
		int d=zz;
		while(D[d].at&&d<=n){
			ll tmp=D[d].at-val[At];ok[At]=true;
			while(tmp<D[d].at)d++;
			At--;
			while(At&&dp[At-1][d])At--;
		}
		for(register int i=1;i<=S;++i)
		    if(ok[i])ans[i]=val[i];
		    else ans[i]=-val[i];
		for(register int i=1;i<=num0;++i)ans[S+i]=0;
		sort(ans+1,ans+S+num0+1);
		printf("Case #%d:",tt);
		for(register int i=1;i<=S+num0;++i)printf(" %lld",ans[i]);
		puts("");
	}
	return 0;
}/*
3
8
0 1 2 3 4 5 6 7
1 1 1 1 1 1 1 1
4
0 1 3 4
4 4 4 4
5
-2 -1 0 1 2
1 2 2 2 1
*/

2019-9-26
Life is like a box of chocolates
C题裸的大数据结构题,不想说了
A:有趣的期望
每次更新若不是当前最小值,那么新的最小值可以很轻易的得出
否则暴力重建
注意,当前有多个最小值取的数都不算最小值
否则概率不是 1 n \frac{1}{n} n1

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef unsigned int uint;
const uint trans=10099;
#define Maxn 10000010
uint num[Maxn],x[Maxn<<1];
uint n,m,a,b,c;
int main(){
	scanf("%u%u%u%u%u%u%u",&n,&m,&x[0],&x[1],&a,&b,&c);
	for(register int i=2;i<=2*m;++i)x[i]=(a*x[i-2]+b*x[i-1]+c);
	for(register int i=0;i<n;++i)num[i]=(1ll<<32)-1;
	uint minv=(1ll<<32)-1,all=n,res=10099,ans=0;
	for(register int i=1;i<=m;++i){
		uint tmp1=(x[i*2-1]>>2)%n;
		uint tmp2=(x[i*2]>>2);
		if(num[tmp1]!=minv){
			if(tmp2<minv){
				minv=tmp2;
				all=1;
			}else if(tmp2==minv){
				all++;
			}
			num[tmp1]=tmp2;
		}else{
			if(all>1){
				all--;
				num[tmp1]=tmp2;
				if(tmp2<minv){
					minv=tmp2;
					all=1;
				}else if(tmp2==minv)all++;
			}else{
				num[tmp1]=tmp2;
				minv=(1ll<<32)-1;
				for(register int j=0;j<n;++j)minv=min(minv,num[j]);
				all=0;
				for(register int j=0;j<n;++j)
				    if(num[j]==minv)all++;
			}
		}
		ans+=minv*res;
		res*=trans;
	}
	printf("%u\n",ans);
	return 0;
}

B:啦啦啦
先删除,再更改,再移位,再加入
好啦,dp啦,怎么dp
s的前i对t的前j
操作1:f[i][j]=f[i][j-1]+a
操作2:f[i][j]=f[i-1][j]+b
操作3:f[i][j]=f[i-1][j-1]+c
由于a+b<=2*d,因此每个数只会交换一次,并且交换后不会替换。(交换后替换跟两个替换比较得交换小于等于替换,而用 a + b < = 2 ∗ d < = d + c a+b<=2*d<=d+c a+b<=2d<=d+c)
操作4:记k为s中上一个t[j]的位置,l为t中上一个s[i]的位置,f[i][j]=f[k-1][l-1]+d+(i-k-1)*b+(j-l-1)*a
这样选k,l最好,别的k和l可以尝试反证
时间复杂度 O ( ∣ s ∣ ∣ t ∣ ) O(|s||t|) O(st)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define Maxn 4005
char S[Maxn],T[Maxn];
int last1[26],last2[26],len1,len2;
int f[Maxn][Maxn],a,b,c,d;
int main(){
	scanf("%d%d%d%d",&a,&b,&c,&d);
	scanf("%s%s",S+1,T+1);
	len1=strlen(S+1);
	len2=strlen(T+1);
	for(register int i=1;i<=len2;++i)f[0][i]=a*i;
	f[0][0]=0;
	for(register int i=1;i<=len1;++i){
		memset(last2,0,sizeof(last2));
		f[i][0]=b*i;
	    for(register int j=1;j<=len2;++j){
	    	f[i][j]=400000000;
	    	f[i][j]=min(f[i][j],f[i][j-1]+a);
	    	f[i][j]=min(f[i][j],f[i-1][j]+b);
	    	f[i][j]=min(f[i-1][j-1]+c,f[i][j]);
	    	if(S[i]==T[j])f[i][j]=min(f[i][j],f[i-1][j-1]);
		    int k=last1[T[j]-'a'],l=last2[S[i]-'a'];
		    if(k&&l)f[i][j]=min(f[i][j],f[k-1][l-1]+d+(i-k-1)*b+(j-l-1)*a);
			last2[T[j]-'a']=j;
		}
		last1[S[i]-'a']=i;
    }
    printf("%d\n",f[len1][len2]);
    return 0;
}

2019-9-27
A:给你一个质数P,C
给你递推式子
S_i= a S i − 2 + b S i − 1 + c aS_{i-2}+bS_{i-1}+c aSi2+bSi1+c的形式
问你n项内有多少个摸P同余C
a=0特判
考虑矩阵乘法
否则你发现这个矩阵可逆
于是像BSGS那样处理出 P P P\sqrt P PP 个位置的矩阵
乘上 0   P − 1 0~\sqrt P-1 0 P 1次方加哈希查找相同
注意到,因为可逆,所以周期一定包含第一个
B:线段树要敢想敢写

2019-9-29
冒泡排序从01序列角度考虑会有奇特效果
C:若有m种货币,找出所有可以组成的价值,且任意两个货币价值的积小于等于10000
最短路建图,货币价值c从小到大排序,在模c1的情况下求最短路,长度不超过10000
这个可以解释noip 2017 day1t1

2019-9-30
从FWT的逆变换角度思考有奇迹
同时如果一道题操作次数较少可以考虑每次一次性进行前面的修改
xor的FWT
F W T ( A 0 + A 1 ) = F W T ( A 0 ) + F W T ( A 1 ) , F W T ( A 0 ) − F W T ( A 0 ) − F W T ( A 1 FWT(A_0+A_1)=FWT(A_0)+FWT(A_1),FWT(A_0)-FWT(A_0)-FWT(A_1 FWT(A0+A1)=FWT(A0)+FWT(A1),FWT(A0)FWT(A0)FWT(A1

2019-10-1
判断第一步可以走哪
考虑一个思路
如果会判断胜负
那么枚举第一步,看看下面能否后手胜

2019-10-3
学会了一个求treap期望高度的方法
h i h_i hi表示i个点的笛卡尔树每个点高度和的期望

f i + 1 = 1 + f i i + 1 + i i + 1 f_{i+1}=1+\frac{f_i}{i+1}+\frac{i}{i+1} fi+1=1+i+1fi+i+1i
那么 f i = i l o g 2 i f_i=ilog_2i fi=ilog2i
那么这启发我们一个好玩的东西
所有点的高度和除以点的个数就是子树大小的期望
没错就是 l o g 2 i log_2i log2i
发现旋转treap似乎也可以持久化

2019-10-5
在这里插入图片描述
在这里插入图片描述
然而我写的比较挫,没有除以a
一些形如FFT的式子,可以考虑用dp求其DFT
在DFT的层面上进行运算
然后再IDFT
一些动态dp中
矩阵的更改中若n*n的行列置换一样
那么可以很快地得到更改后的乘积

2019-10-8
A题:agc004-e

利用模的性质可以把 f i f_i fi表示成 l o g m log_m logm个和
然后后面的就可以很好地处理了
C题:AGC 004 F Namori
神题!

2019-10-9
A题,在这里插入图片描述
在这里插入图片描述
有时候树形dp的过程可以用直径简化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值