• 省选联考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;
    }
    
  • 相关阅读:
    Python--初识函数
    Python中的文件操作
    Python中的集合
    Python中的编码和解码
    Python的关键字is和==
    Python中的字典
    Python中的列表和元组
    Python中几种数据的常用内置方法
    Python的编码
    python_while
  • 原文地址:https://www.cnblogs.com/WinterSpell/p/13191022.html
Copyright © 2020-2023  润新知