省选联考2020简要题解

被巨佬锤爆了啊啊啊啊
脑力不足很多比较简单的东西想不到
国赛出现类似的知识点概率不大 但还是订正一下吧

冰火战士(A/B卷)

我考场写的垃圾代码竟然能过 感谢少爷机
可以将冰与火做差 得到的函数单调不降
找到最大的差值小于零的温度t 还得和t+1的答案比较谁更优
然后还得往右移(好像这里暴力往右移得满分?)
然后常数就大爆炸……
其实树状数组也是可以一个log二分的(可惜我不会)如果写了这个就能稳过了 还可以有点剪枝啥的
遇到这种2e6还无O2的毒瘤题就千万别用set和map了……

#include<bits/stdc++.h>
using namespace std;
#define fp(i,l,r) for(register int (i)=(l);i<=(r);++(i))
#define fd(i,l,r) for(register int (i)=(l);i>=(r);--(i))
#define fe(i,u) for(register int (i)=front[(u)];(i);(i)=e[(i)].next)
#define mem(a) memset((a),0,sizeof (a))
#define O(x) cerr<<#x<<':'<<x<<endl
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return x*f;
}
void wr(int x){
	if(x<0)x=-x,putchar('-');
	if(x>=10)wr(x/10);
	putchar('0'+x%10);
}
const int MAXN=2e6+10;
struct node{
	int op,t,x,y;
}q[MAXN];
int Q,buc[MAXN],sz,sum;
struct BIT{
	int c[MAXN];
	inline int ask(int i){
		int res=0;
		for(;i;i^=i&-i)
		res+=c[i];
		return res;
	}
	inline void add(int i,int v){
		for(;i<=sz;i+=i&-i)
		c[i]+=v;
	}
}T[2];
inline int gval(int x){
	int l=1,r=sz,res=sz+1;
	while(l<=r){
		int mid=l+r>>1;
		if(buc[mid]>=x)r=mid-1,res=mid;
		else l=mid+1;
	}
	return res;
}
main(){
	Q=read();
	fp(i,1,Q){
		q[i].op=read(),q[i].t=read();
		if(q[i].op==1){
			buc[++sz]=q[i].x=read();q[i].y=read();
		}
	}
	sort(buc+1,buc+1+sz);sz=unique(buc+1,buc+1+sz)-buc-1;
	fp(i,1,Q)q[i].x=gval(q[i].x);
	fp(i,1,Q){
		int op=q[i].op,tt=q[i].t,x,y;
		if(op==1)x=q[i].x,y=q[i].y;
		else x=q[tt].x,y=-q[tt].y,tt=q[tt].t;
		if(tt==0)T[0].add(x,y);
		else sum+=y,T[1].add(x+1,y);
		int val=-sum,p=0;
		fd(j,20,0)if(p+(1<<j)<=sz){
			int t=val+T[0].c[p+(1<<j)]+T[1].c[p+(1<<j)];
			if(t<0)val=t,p+=1<<j;
		}
		int res1=2*T[0].ask(p),ans=res1;
		if(p<sz){
			int res2=2*(sum-T[1].ask(p+1));
			if(res2>=res1){
				ans=res2;
				int tar=T[1].ask(p+1);
				p=0;val=0;
				fd(j,20,0)if(p+(1<<j)<=sz){
					int t=val+T[1].c[p+(1<<j)];
					if(t<=tar)val=t,p+=1<<j;
				}
			}
		}
		if(!ans)puts("Peace");
		else wr(buc[p]),putchar(' '),wr(ans),putchar('
');
	}
	return 0;
}

组合数问题(A卷)

大佬口中的傻逼题 我就怎么都不会
肯定是先化成(sum_{i=0}^{m}a_isum_{k=0}^{n}k^ix^kinom{n}{k})
重点是对于(iin[0,m])求出(f_i=sum_{k=0}^nk^ix^kinom{n}{k})
以下做法基本都是从别的地方抄的
做法一:
(i=0)的情况是二项式定理 (f_i)等于(f_{i-1})求个导什么的
sto szq orz
做法二:
其实看到幂函数就应该想到斯特林数的 我连这一步都没做到
这里(S(i,j))表示第二类斯特林数

[f_i=sum_{k=0}^nx^kinom{n}{k}sum_j S(i,j) inom{k}{j}j! ]

[=sum_jS(i,j)j!sum_{k=0}^nx^kinom{n}{k}inom{k}{j} ]

[=sum_jS(i,j)j!inom{n}{j}sum_{k=j}^nx^kinom{n-j}{k-j} ]

[=sum_jS(i,j)j!inom{n}{j}x^jsum_{k=0}^{n-j}x^kinom{n-j}{k} ]

[=sum_jS(i,j)n^{underline{j}}x^j(x+1)^{n-j} ]

做法三:
(f_i)的EGF(F(z))

[F(z)=sum_ifrac{z^i}{i!}sum_kinom{n}{k}x^kk^i ]

[=sum_kinom{n}{k}x^ksum_ifrac{(kz)^i}{i!} ]

[=sum_kinom{n}{k}x^ke^{kz} ]

[=sum_kinom{n}{k}(xe^z)^k ]

[=(xe^z+1)^n ]

如果模数取998244353可以做到(O(mlog m))
做法四:
考虑(f_i)组合意义:有(n)个不同的盒子 从这些盒子中选出(k)个 给这(k)个盒子染上(x)种颜色 把(i)个有标号的球放入(k)个盒子中
每个盒子的EGF(F(z))(1+xe^z) 相当于枚举它是否被选中,放入了几个球
好像和上一个做法一模一样

#include<bits/stdc++.h>
using namespace std;
#define fp(i,l,r) for(register int (i)=(l);i<=(r);++(i))
#define fd(i,l,r) for(register int (i)=(l);i>=(r);--(i))
#define fe(i,u) for(register int (i)=front[(u)];(i);(i)=e[(i)].next)
#define mem(a) memset((a),0,sizeof (a))
#define O(x) cerr<<#x<<':'<<x<<endl
#define int long long
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return x*f;
}
void wr(int x){
	if(x<0)x=-x,putchar('-');
	if(x>=10)wr(x/10);
	putchar('0'+x%10);
}
const int MAXN=1010;
int S[MAXN][MAXN],n,X,mod,m,a[MAXN],pw[MAXN];
inline void tmod(int &x){x%=mod;}
inline int qpow(int a,int b){
	int res=1;a%=mod;
	for(;b;b>>=1,tmod(a*=a))
	if(b&1)tmod(res*=a);
	return res;
}
main(){
	n=read();X=read();mod=read();m=read();
	fp(i,0,m)a[i]=read();
	S[0][0]=1;pw[0]=qpow(X+1,n-m);
	fp(i,1,m)pw[i]=pw[i-1]*(X+1)%mod;
	fp(i,1,m){
		S[i][1]=1;
		fp(j,2,i)
		tmod(S[i][j]=S[i-1][j-1]+j*S[i-1][j]);
	}
	int ans=0;
	fp(i,0,m){
		int tx=1,tn=1,sum=0;
		fp(j,0,i){
			tmod(sum+=S[i][j]*tn%mod*tx%mod*pw[m-j]);
			tmod(tn*=n-j);tmod(tx*=X);
		}
		tmod(ans+=sum*a[i]);
	}
	printf("%lld
",ans);
	return 0;
}

魔法商店(A卷)

我怎么可能会…… 以后有时间再做吧

信号传递(A/B卷)

把一次传递的代价分摊给两个信号塔
(g_S)表示用完(S)这个集合填充前(|S|)个位置的最小代价
需要预处理一个(h_{i,S})表示从(g_S)转移到(g_{Scup i})花费的额外代价
然后就会被卡空间……
考虑把(h_{i,S})折半 就同时优化了空间复杂度和时间上的常数(我怎么还是想不到)

#include<cstdio>
#include<cstring>
using namespace std;
#define fp(i,l,r) for(register int (i)=(l);(i)<=(r);++(i))
#define fd(i,l,r) for(register int (i)=(l);(i)>=(r);--(i))
#define fe(i,u) for(register int (i)=front[(u)];i;i=e[i].next)
#define mem(a) memset((a),0,sizeof (a))
#define O(x) cerr<<#x<<':'<<x<<endl
//#define int long long
inline bool isdigit(char ch){return ch>='0'&&ch<='9';}
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return x*f;
}
void wr(int x){
	if(x<0)x=-x,putchar('-');
	if(x>=10)wr(x/10);
	putchar('0'+x%10);
}
int g[1<<23],n,m,K,s[100010],cur;
int E[25][25],a[23][1<<12],b[23][1<<11];short bcnt[1<<23],lg[1<<23];
inline int min(int x,int y){return x<y?x:y;}
main(){
	n=read();m=read();K=read();
	fp(i,1,n){
		s[i]=read()-1;if(i==1)continue;
		++E[s[i-1]][s[i]];
	}
	int c1=(m+1)/2,s1=(1<<c1)-1,c2=m-c1,s2=(1<<c2)-1;
	int S=(1<<m)-1;
	fp(i,2,S)lg[i]=lg[i/2]+1;
	fp(i,1,S)bcnt[i]=bcnt[i^(i&-i)]+1;
	fp(i,0,m-1){
		fp(j,0,s1){
			fp(k,0,c1-1)if(k!=i){
				if(!(j>>k&1))a[i][j]+=E[k][i]*K-E[i][k];
				else a[i][j]+=E[k][i]+E[i][k]*K;
			}
		}
		if(!c2)continue;
		fp(j,0,s2){
			fp(k,c1,m-1)if(k!=i){
				if(!(j>>k-c1&1))b[i][j]+=E[k][i]*K-E[i][k];
				else b[i][j]+=E[k][i]+E[i][k]*K;
			}
		}
	}
	fp(i,1,S)g[i]=1e9;
	fp(i,0,S){
		int tt=S^i,p=bcnt[i]+1;
		for(;tt;tt^=(tt&-tt)){
			int t=lg[tt&-tt],s=i|(1<<t);
			g[s]=min(g[s],g[i]+(a[t][i&s1]+b[t][i>>c1])*p);
		}
	}
	printf("%d
",g[S]);
	return 0;
}

树(A卷)

又是巨佬口中的辣鸡题 又有相当多的做法(但我就是一个都想不到)
trie树那个做法的trick我考前还刚刚看过 结果不知道为什么压根没想到
做法一
我们要支持

  • 全局加一
  • 合并
  • 插入

可以维护一个从低位到高位的trie 复杂度(O(nlog n)) 不过据说干不过两个log

做法二
拆开每一位计算贡献 假设这个点点权是(v) 好像可以发现(v,v+1,v+2,cdots)有循环节
可以找出贡献区间 然后发现它们模(2^{k+1})意义下是同一段区间
然后用天天爱跑步的技巧 dfs时出来的数组值异或上刚进入时的数组值
复杂度也可以做到(O(nlog n)) 但常数极小
理解不是很透彻…… 可以看看这个:https://www.luogu.com.cn/blog/dengyaotriangle/solution-p6623
有加一操作! 我trie深度还设小了

#include<bits/stdc++.h>
using namespace std;
#define fp(i,l,r) for(register int (i)=(l);(i)<=(r);++(i))
#define fd(i,l,r) for(register int (i)=(l);(i)>=(r);--(i))
#define fe(i,u) for(register int (i)=front[(u)];i;i=e[i].next)
#define mem(a) memset((a),0,sizeof (a))
#define O(x) cerr<<#x<<':'<<x<<endl
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return x*f;
}
void wr(int x){
	if(x<0)x=-x,putchar('-');
	if(x>=10)wr(x/10);
	putchar('0'+x%10);
}
const int MAXN=525020;
int n,val[MAXN],ch[MAXN*30][2],res[MAXN*30],tot;
bool vis[MAXN*30];
int rt[MAXN],fa[MAXN];long long ans;
vector<int>son[MAXN];
#define ls ch[o][0]
#define rs ch[o][1]
inline void pushup(int o,int d){
	res[o]=res[ls]^res[rs]^(vis[rs]?1<<d:0);
}
void add(int &o,int x,int d){
	if(!o)o=++tot;
	vis[o]^=1;
	if(d<21)add(ch[o][x>>d&1],x,d+1);
	pushup(o,d);
}
void merge(int &o1,int o2,int d){
	if(!o1||!o2)return void(o1|=o2);
	vis[o1]^=vis[o2];
	merge(ch[o1][0],ch[o2][0],d+1);merge(ch[o1][1],ch[o2][1],d+1);
	pushup(o1,d);
}
void add1(int o,int d){
	if(!o)return;swap(ls,rs);
	if(d<21)add1(ls,d+1);
	pushup(o,d);
}
void dfs(int u){
	for(int v:son[u]){
		dfs(v);merge(rt[u],rt[v],0);
	}
	add1(rt[u],0);add(rt[u],val[u],0);
	ans+=res[rt[u]];
}
main(){
	n=read();
	fp(i,1,n)val[i]=read();
	fp(i,2,n){
		fa[i]=read();son[fa[i]].push_back(i);
	}
	dfs(1);
	printf("%lld
",ans);
	return 0;
}

作业题(A卷)

有一次我不会求生成树边权和 $gg告诉了我
然后我考场上又忘记了如何求生成树边权和 写了和那一次一摸一样的暴力……

我怎么都不知道(varphi = mu * Id) 当时用(mu)写的……

[ans=sum_d varphi(d)F(d) ]

(F(d))表示只选取d倍数边的生成树边权和
重点是求(F)
将每条边边权设为(wx+1) 答案是一次项系数
对于除法:((1-ax)^{-1} equiv 1+ax pmod{x^2})
((ax+b)^{-1} equiv -frac{a}{b^2}x+b^{-1} pmod{x^2})
这时我们开始怀疑复杂度是不是跟暴力枚举每条边一样也是(O(n^3md))的 因为可能总共有(md)个位置需要算一遍行列式
我们只需要对边数(ge n-1)(d)求行列式 这样复杂度最高是(O(n^4d)的)
我简直是重度脑残…… 先是没开long long 又是线性筛写错 不知道干什么了
UPD:之前的代码被hack了 现在应该没问题

#include<bits/stdc++.h>
using namespace std;
#define fp(i,l,r) for(register int (i)=(l);(i)<=(r);++(i))
#define fd(i,l,r) for(register int (i)=(l);(i)>=(r);--(i))
#define fe(i,u) for(register int (i)=front[(u)];i;i=e[i].next)
#define mem(a) memset((a),0,sizeof (a))
#define O(x) cerr<<#x<<':'<<x<<endl
#define int long long
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return x*f;
}
void wr(int x){
	if(x<0)x=-x,putchar('-');
	if(x>=10)wr(x/10);
	putchar('0'+x%10);
}
const int mod=998244353,MAXW=152510;
inline void tmod(int &x){x%=mod;}
inline int qpow(int a,int b){
	int res=1;a%=mod;
	for(;b;b>>=1,tmod(a*=a))
	if(b&1)tmod(res*=a);
	return res;
}
inline int inv(int x){return qpow(x,mod-2);}
int phi[MAXW],p[MAXW/5],pcnt;
bool np[MAXW];
inline void sieve(int n){
	phi[1]=np[1]=np[0]=1;
	fp(i,2,n){
		if(!np[i])p[++pcnt]=i,phi[i]=i-1;
		fp(j,1,pcnt){
			int t=p[j]*i;if(t>n)break;
			np[t]=1;if(i%p[j]==0){phi[t]=phi[i]*p[j];break;}
			phi[t]=phi[i]*(p[j]-1);
		}
	}
}
struct pr{
	int a,b;
	inline pr(int x=0,int y=0){a=x,b=y;}
	inline operator bool()const{return a||b;}
	inline pr &operator+=(const pr&x){tmod(a+=x.a);tmod(b+=x.b);return *this;}
	inline pr &operator-=(const pr&x){tmod(a-=x.a);tmod(b-=x.b);return *this;}
	inline pr &operator*=(const pr&x){tmod(a=a*x.b+b*x.a);tmod(b*=x.b);return *this;}
	inline pr &operator/=(const pr&x){
		int t=inv(x.b);
		tmod(b*=t);tmod(a=(a-b*x.a)%mod*t);
		return *this;
	}
}g[32][32];
inline pr operator * (pr x,pr y){return {(x.a*y.b+x.b*y.a)%mod,x.b*y.b%mod};}
inline int det(int n){
	pr res=pr(0,1);int fl=1;
	fp(i,1,n){
		int p=0;
		fp(j,i,n)if(g[j][i]){p=j;break;}
		if(!p)return 0;
		if(p!=i)swap(g[p],g[i]),fl*=-1;
		res*=g[i][i];
		fp(j,i+1,n){
			pr t=g[j][i];t/=g[i][i];
			fp(k,i,n)g[j][k]-=t*g[i][k];
		}
	}
	return fl*res.a;
}
int fa[32],n,qu[1010],qv[1010],qw[1010],m;
int getf(int x){return x==fa[x]?x:fa[x]=getf(fa[x]);}
inline void merge(int a,int b){
	a=getf(a);b=getf(b);if(a==b)return;
	fa[a]=b;
}
vector<int>bb[MAXW];
inline void Add(int x,int y,int w){
	g[x][y]-=pr(w,1);g[y][x]-=pr(w,1);
	g[x][x]+=pr(w,1);g[y][y]+=pr(w,1);
}
inline int solve(int p){
	if(bb[p].size()<n-1)return 0;
	fp(i,1,n)fa[i]=i;
	for(auto i:bb[p])merge(qu[i],qv[i]);
	int cc=0;fp(i,1,n)if(fa[i]==i)++cc;if(cc!=1)return 0;
	mem(g);
	for(auto i:bb[p])Add(qu[i],qv[i],qw[i]);
	return det(n-1);
}
inline void insert(int w,int id){
	fp(i,1,w){
		if(i*i>w)return;if(w%i)continue;
		bb[i].push_back(id);if(i!=w/i)bb[w/i].push_back(id);
	}
}
main(){
	n=read();m=read();sieve(MAXW-3);
	fp(i,1,m){
		qu[i]=read(),qv[i]=read();qw[i]=read();
		insert(qw[i],i);
	}
	int ans=0;
	fp(i,1,MAXW-3){
		tmod(ans+=phi[i]*solve(i));
	}
	printf("%lld
",(ans+mod)%mod);
	return 0;
}
原文地址:https://www.cnblogs.com/misaka10047/p/13191022.html