NOIP模拟73

T1 小L的疑惑

解题思路

第一眼不是正解,又是 bitset 优化可以得到的 60pts 的部分分。

打着打着突然发现这个东西好像和之前做过的某个题有一些相似,试着打了一下。

然后样例过了,然后对拍没错,然后就切了??

先排序,如果某个数之前所有数的和都比这个数字-1 小,那么这里就是一个空缺,扫的时候选择最小的空缺就是答案。

code

#include <bits/stdc++.h>
#define int long long 
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=1e5+10,M=1e7+10,INF=1e18;
int n,cnt,s[N];
signed main()
{
	freopen("math.in","r",stdin); freopen("math.out","w",stdout);
	n=read(); for(int i=1;i<=n;i++) s[i]=read(); sort(s+1,s+n+1);
	for(int i=1;i<=n;i++)
	{
		if(cnt<s[i]-1) printf("%lld",cnt+1),exit(0);
		cnt+=s[i];
	}
	printf("%lld",cnt+1);
	return 0;
}

T2 小L的数列

解题思路

发现运算都是乘法,于是我们考虑转化成为指数上的加法。

然后我们又看了一下 k 的范围,大概是矩阵乘法没错了,于是很好出来下移的做法复杂度大概是 \(k^3log(n-k)\)

但是我考场上一时糊涂竟然没有想到,于是整到了一个 \(k^3log\frac{n-k}{k}\) 的做法,也就是一次性转移 k 个。

单位矩阵的第一行就是 \(b_k,b_{k-1},...b_2,b_1\) 第二行其实就是 \(0,b_{k},...b_3,b_2\) 再加上第一行的系数每一项乘上一个 \(b_1\) 其它的系数也是类似。

然后我们就可以一次性转移 k 个了,免去了卡常的麻烦。

code

#include <bits/stdc++.h>
#define int long long 
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=4e7+10,M=210,mod=998244353;
int n,m,Ans=1,f[M],b[M];
struct Square
{
	int a[M][M];
	void clear(){memset(a,0,sizeof(a));}
	Square friend operator * (Square x,Square y)
	{
		Square z; z.clear();
		for(int i=1;i<=m;i++)
			for(int j=1;j<=m;j++)
				for(int k=1;k<=m;k++)
					z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j])%(mod-1);
		return z;
	}
}e,ans;
void pw(Square &x,int y)
{
	while(y)
	{
		if(y&1) x=x*e;
		e=e*e; y>>=1;
	}
}
int power(int x,int y,int p=mod)
{
	int temp=1;
	while(y)
	{
		if(y&1) temp=temp*x%mod;
		x=x*x%mod; y>>=1;
	}
	return temp;
}
signed main()
{
	freopen("seq.in","r",stdin); freopen("seq.out","w",stdout);
	n=read(); m=read();
	for(int i=1;i<=m;i++) b[i]=read();
	for(int i=1;i<=m;i++) f[i]=read();
	for(int i=1;i<=m;i++) ans.a[i][i]=1;
	for(int i=1;i<=m;i++)
	{
		for(int j=i;j<=m;j++) e.a[i][j]=b[m-(j-i)];
		for(int j=1;j<i;j++)
			for(int k=1;k<=m;k++)
				e.a[i][k]=(e.a[i][k]+e.a[j][k]*b[i-j])%(mod-1);
	}
	pw(ans,n/m+(n%m!=0)-1);
	int pos=n%m; if(!pos) pos=m;
	for(int i=1;i<=m;i++)
		Ans=Ans*power(f[i],ans.a[pos][i])%mod;
	printf("%lld",Ans);
	return 0;
}

T3 连边

解题思路

尽管实际得分远高于期望得分,但是还是挂了 20pts 没有场上切掉(感觉不稳就判了一下暴力,然后暴力被卡常了。。)

大概就是一个多源最短路同时记录一下前驱,对于只有一个前驱的直接选择,对于多个前驱的选择权值最小的。。

本来我只是想对于随机数据下手的,想随便 rand() 一下,但是感觉不稳,就贪了个心,然后歪打正着??(算是吧。。)

code

#include <bits/stdc++.h>
#define int long long 
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=2e5+10,M=N<<1,INF=1e18;
int n,m,ans=INF,dis[N],fa[N];
int tot=1,head[N],nxt[M<<1],ver[M<<1],edge[M<<1];
bool col[N],vis[N],can[M];
struct Road{int l,r,val;}pat[M];
priority_queue<pair<int,int> > q;
vector<pair<int,int> > pre[N];
void add_edge(int x,int y,int val)
{
	ver[++tot]=y; edge[tot]=val;
	nxt[tot]=head[x]; head[x]=tot;
}
int find(int x)
{
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
signed main()
{
	freopen("minimum.in","r",stdin); freopen("minimum.out","w",stdout);
	n=read(); m=read();
	for(int i=1;i<=n;i++) vis[i]=col[i]=read(),fa[i]=i;
	for(int i=1,x,y,val;i<=m;i++)
	{
		x=read(); y=read(); val=read(); pat[i]=(Road){x,y,val},
		add_edge(x,y,val); add_edge(y,x,val);
		if(find(x)==find(y)) continue;
		vis[find(y)]|=vis[find(x)];
		fa[find(x)]=find(y);
	}
	for(int i=1;i<=n;i++) if(!vis[find(i)]) printf("impossible"),exit(0);
	memset(dis,0x3f,sizeof(dis)); memset(vis,false,sizeof(vis));
	for(int i=1;i<=n;i++) if(col[i]) dis[i]=0,q.push(make_pair(0,i));
	while(!q.empty())
	{
		int x=q.top().second; q.pop();
		if(vis[x]) continue; vis[x]=true;
		for(int i=head[x];i;i=nxt[i])
		{
			int to=ver[i],val=edge[i];
			if(dis[to]<dis[x]+val) continue;
			if(dis[to]>dis[x]+val) vector<pair<int,int> >().swap(pre[to]);
			dis[to]=dis[x]+val; pre[to].push_back(make_pair(x,i));
			if(!vis[to]) q.push(make_pair(-dis[to],to));
		}
	}
	for(int i=1;i<=n;i++) if(pre[i].size()==1) can[pre[i][0].second>>1]=true;
	for(int i=1;i<=n;i++)
		if(pre[i].size()!=1)
		{
			int minn=INF,id=0;
			for(int j=0;j<pre[i].size();j++) if(can[pre[i][j].second>>1]) goto X;
			for(int j=0;j<pre[i].size();j++) if(minn>edge[pre[i][j].second]) id=pre[i][j].second,minn=edge[id],id>>=1;
			can[id]=true; X:;
		}
	ans=0;
	for(int i=1;i<=m;i++) ans+=can[i]*pat[i].val;
	printf("%lld",ans);
	return 0;
}

T4 小L的有向图

解题思路

看到 T4 的时候只有 30min 了,感觉不是特别好搞,我直接 printf("0") 然后去看前面的了。。

考完之后听了一下正解思路感觉神似竞赛图,但又不完全是。

\(f_{S}\) 表示 \(S\) 点集内部合法拓扑序的个数然后枚举集合外面的点,计算集合内点到对应点的连边数量作为 2 的指数就是这个点的贡献。

code

#include <bits/stdc++.h>
#define int long long 
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=25,mod=998244353;
int n,m,e[N],f[1<<22],sum[1<<22];
int lowbit(int x){return x&(-x);}
signed main()
{
	freopen("topology.in","r",stdin); freopen("topology.out","w",stdout);
	n=read(); m=read();
	for(int i=1,x,y;i<=m;i++) x=read(),y=read(),e[y]|=1<<x-1;
	for(int i=1;i<=n;i++) sum[1<<i-1]=1; f[0]=1;
	for(int i=1;i<(1<<n);i++)sum[i]=sum[i^lowbit(i)]+sum[lowbit(i)];
	for(int sta=0;sta<(1<<n);sta++)
		for(int i=1;i<=n;i++)
		{
			if((sta>>i-1)&1) continue;
			int temp=sum[sta&e[i]];
			f[sta|(1<<i-1)]=(f[sta|(1<<i-1)]+f[sta]*(1ll<<temp))%mod;
		}
	printf("%lld",f[(1<<n)-1]);
	return 0;
}

T? 中国象棋

解题思路

由于今天的考试题目比较简单,教练就又加了几道题,看了看好像就这道比较好做,大致口胡一下。

显然每行每列最多只可能有两个棋子,于是设 \(f_{i,j,k}\) 表示前 i 行,有 j 列只有一个棋子,有 k 列只有两个棋子。

然后分别枚举不放旗子,在有一个棋子的列放一个,在没有的放一个或者两个,在没有的有一个的各放一个,在有一个的放两列。

code

#include <bits/stdc++.h>
#define int long long 
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=110,mod=9999973;
int n,m,ans,f[N][N][N];
void add(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int C2(int x){return x*(x-1)/2;}
void solve(int i,int j,int k)
{
	if(!f[i][j][k]) return ;
	add(f[i+1][j][k],f[i][j][k]);
	add(f[i+1][j+1][k],f[i][j][k]*(m-j-k)%mod);
	add(f[i+1][j+2][k],f[i][j][k]*C2(m-j-k)%mod);
	if(j>=2) add(f[i+1][j-2][k+2],f[i][j][k]*C2(j)%mod);
	if(j>=1) add(f[i+1][j-1][k+1],f[i][j][k]*j%mod),add(f[i+1][j][k+1],f[i][j][k]*(m-j-k)%mod*j%mod);
}
signed main()
{
	freopen("chess.in","r",stdin); freopen("chess.out","w",stdout);
	n=read(); m=read(); f[0][0][0]=1;
	for(int i=0;i<n;i++) for(int j=0;j<=m;j++) for(int k=0;k<=m-j;k++) solve(i,j,k);
	for(int i=0;i<=m;i++) for(int j=0;j<=m-i;j++) add(ans,f[n][i][j]);
	printf("%lld",ans); return 0;
}
posted @ 2021-10-10 21:25  Varuxn  阅读(99)  评论(0编辑  收藏  举报