2021.10.10考试总结[NOIP模拟73]

T1 小L的疑惑

对于\(P_i\),如果所有比\(P_i\)小的数加起来也达不到\(P_i-1\),那么值域肯定不连续。否则设原来值域最大值为\(mx\),则\(P_i\)会让值域最大值增致\(mx+P_i\)。

排序后扫一遍。

\(code:\)

T1
#include<bits/stdc++.h>
#define int long long
using namespace std;

namespace IO{
	auto read=[]()->int{
		char ch=getchar(); int x=0,f=1;
		while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
		while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
		return x*f;
	};
	auto write=[](int x,int sp)->void{
		char ch[20]; int len=0;
		if(x<0){ x=~x+1; putchar('-'); }
		do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
		for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
	};
	auto ckmax=[](int& x,int y)->void{ x=x<y?y:x; };
	auto ckmin=[](int& x,int y)->void{ x=x<y?x:y; };
} using namespace IO;

const int NN=100010;
int n,p[NN],f[NN];

signed main(){
	freopen("math.in","r",stdin);
	freopen("math.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++) p[i]=read();
	sort(p+1,p+n+1);
	for(int i=1;i<=n;i++){
		if(f[i-1]<p[i]-1) return write(f[i-1]+1,'\n'),0; 
		f[i]=f[i-1]+p[i];
	}
	return write(f[n]+1,'\n'),0;
}

T2 小L的数列

把乘法化为指数加法,就是挺裸的矩阵快速幂优化了。

注意指数上应模\(998244352\)。由费马小定理不难得出\(x^{p-1}\equiv x(\mod p)\)。

\(code:\)

T2
#include<bits/stdc++.h>
using namespace std;

namespace IO{
	auto read=[]()->int{
		char ch=getchar(); int x=0,f=1;
		while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
		while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
		return x*f;
	};
	auto write=[](int x,int sp)->void{
		char ch[20]; int len=0;
		if(x<0){ x=~x+1; putchar('-'); }
		do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
		for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
	};
	auto ckmax=[](int& x,int y)->void{ x=x<y?y:x; };
	auto ckmin=[](int& x,int y)->void{ x=x<y?x:y; };
} using namespace IO;

const int NN=210,p=998244353,mod=p-1;
int n,k,ans,b[NN],f[NN];
int qpow(int a,int x){
	int res=1;
	for(;x;x>>=1){
		if(x&1) res=1ll*res*a%p;
		a=1ll*a*a%p;
	}
	return res;
}

namespace Matrix{
	struct mat{
		int s[NN][NN];
		mat(){}
		mat(int x){ memset(s,0,sizeof(s)); for(int i=1;i<=k;i++) s[i][i]=x; }
		mat operator*(const mat& a)const{
			mat res=mat(0);
			for(int i=1;i<=k;i++)
				for(int l=1;l<=k;l++)
					for(int j=1;j<=k;j++)
						(res.s[i][j]+=1ll*s[i][l]*a.s[l][j]%mod)%=mod;
			return res;
		}
	}t;
	mat operator^(mat a,int x){
		mat res=mat(1);
		for(;x;x>>=1){
			if(x&1) res=res*a;
			a=a*a;
		}
		return res;
	}
	void prework(){
		for(int i=1;i<k;i++) t.s[i+1][i]=1;
		for(int i=1;i<=k;i++) t.s[i][k]=b[k-i+1];
	}
} using namespace Matrix;

signed main(){
	freopen("seq.in","r",stdin);
	freopen("seq.out","w",stdout);
	n=read(); k=read(); ans=1;
	for(int i=1;i<=k;i++) b[i]=read();
	for(int i=1;i<=k;i++) f[i]=read();
	if(n<=k) return write(f[n],'\n'),0;
	prework(); t=t^(n-k);
	for(int i=1;i<=k;i++)
		ans=1ll*ans*qpow(f[i],t.s[i][k])%p;
	return write(ans,'\n'),0;
}
/*
5 4
1 2 3 4
4 3 2 1

100000 4
1 2 3 4
12 23 34 45

*/

T3 连边

以黑点为源点做多源最短路。

考虑多加一个白点的贡献,其实就是白点向前驱连了一条边。因此做最短路时记录每个点在最短路中与它连边最短的前驱,最后累加答案即可。

\(code:\)

T3
#include<bits/stdc++.h>
#define int long long
using namespace std;

namespace IO{
	auto read=[]()->int{
		char ch=getchar(); int x=0,f=1;
		while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
		while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
		return x*f;
	};
	auto write=[](int x,int sp)->void{
		char ch[20]; int len=0;
		if(x<0){ x=~x+1; putchar('-'); }
		do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
		for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
	};
	auto ckmax=[](int& x,int y)->void{ x=x<y?y:x; };
	auto ckmin=[](int& x,int y)->void{ x=x<y?x:y; };
} using namespace IO;

const int NN=100010;
int n,m,ans;
bool col[NN];

namespace graph{
	#define x first
	#define y second
	#define mp make_pair
	#define pb push_back
	typedef pair<int,int> PII;
	const int MM=NN<<2;
	priority_queue<PII,vector<PII>,greater<PII>>q;
	int idx,to[MM],nex[MM],w[MM],head[NN],dis[NN],mn[NN];
	bool vis[NN];
	void add(int a,int b,int c){
		to[++idx]=b; nex[idx]=head[a]; head[a]=idx; w[idx]=c;
		to[++idx]=a; nex[idx]=head[b]; head[b]=idx; w[idx]=c;
	}
	void dijstra(){
		memset(dis,0x3f,sizeof(dis));
		memset(mn,0x3f,sizeof(mn));
		for(int i=1;i<=n;i++) if(col[i])
			dis[i]=0, q.push(mp(0,i));
		while(!q.empty()){
			int x=q.top().y,y=q.top().x; q.pop();
			if(vis[x]) continue;
			vis[x]=1;
			for(int i=head[x];i;i=nex[i]){
				int v=to[i];
				if(dis[v]==y+w[i]) ckmin(mn[v],w[i]);
				else if(dis[v]>y+w[i]){
					dis[v]=y+w[i];
					mn[v]=w[i];
					q.push(mp(dis[v],v));
				}
			}
		}
	}
} using namespace graph;

void getans(){
	for(int i=1;i<=n;i++) if(!col[i]){
		if(mn[i]>1e17) puts("impossible"),exit(0);
		ans+=mn[i];
	}
}

signed main(){
	freopen("minimum.in","r",stdin);
	freopen("minimum.out","w",stdout);
	n=read(); m=read();
	for(int i=1;i<=n;i++) col[i]=read();
	for(int a,b,c,i=1;i<=m;i++)
		a=read(),b=read(),c=read(),add(a,b,c);
	dijstra(); getans();
	return write(ans,'\n'),0;
}
/*
5 7
0 1 0 1 0
1 2 2
1 3 1
1 5 17
2 3 1
3 5 18
4 5 3
2 4 5

*/

T4 小L的有向图

状压\(DP\),状态记已考虑的点集。

每次考虑在枚举到的点集\(S\)的拓扑序后加入一个点\(i\),那么\(S\)内连向\(i\)的边可删可不删,其它边必须删。

找到这个边数\(x\),将方案数乘上\(2^x\)转移刷表。

\(code:\)

T4
#include<bits/stdc++.h>
#define int long long
using namespace std;

namespace IO{
	auto read=[]()->int{
		char ch=getchar(); int x=0,f=1;
		while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
		while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
		return x*f;
	};
	auto write=[](int x,int sp)->void{
		char ch[20]; int len=0;
		if(x<0){ x=~x+1; putchar('-'); }
		do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
		for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
	};
	auto ckmax=[](int& x,int y)->void{ x=x<y?y:x; };
	auto ckmin=[](int& x,int y)->void{ x=x<y?x:y; };
} using namespace IO;

const int NN=23,MM=470,p=998244353;
int n,m,ans,in[NN],pw[MM],f[1<<22];

signed main(){
	freopen("topology.in","r",stdin);
	freopen("topology.out","w",stdout);
	n=read(); m=read(); pw[0]=f[0]=1;
	for(int a,b,i=1;i<=m;i++)
		a=read(),b=read(),in[b]|=1<<a-1,pw[i]=(pw[i-1]<<1)%p;
	for(int u=0;u<(1<<n);u++)
		for(int i=1;i<=n;i++) if(!(u&(1<<i-1))){
			int tmp=__builtin_popcount(u&in[i]);
			(f[u|(1<<i-1)]+=f[u]*pw[tmp])%=p;
		}
	return write(f[(1<<n)-1],'\n'),0;
}
/*
3 3
1 2
2 3
3 1

*/
上一篇:第五章循环结构程序设计


下一篇:C语言案例:密码设置及登录> 明解getchar与scanf