NOI online #1 入门组

文具订购

题意:

商店有\(7\)元的圆规,\(4\)元的笔,\(3\)元的笔记本。

问恰好用完\(n\)元钱,配套物品最多的情况下,最多能买多少物品?

\(n\leq 10^5\)

题解:

枚举买多少套配套物品,然后算剩下的钱最多能买几个笔记本和笔。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define lowbit(i) ((i)&(-i))
	const int N=1e5+10;
	int n;
	int dp[N],pre[N];
	int d[3]={3,4,7};
	inline void main()
	{
		cin>>n;
		memset(dp,-0x3f,sizeof(dp));
		dp[0]=0;
		for(int i=1;i<=n;++i)
		{
			for(int k=0;k<3;++k) if(i>=d[k])
			{
				if(dp[i-d[k]]+1>dp[i]) dp[i]=dp[i-d[k]]+1,pre[i]=i-d[k];
			}
		}
		int tot=-1,ans=0;
		for(int k=0;k<=n/14;++k)
		{
			int m=n-k*14;
			if(dp[n-14*k]>=0) tot=3*k+dp[m],ans=k;
		}
		if(tot==-1)
		{
			cout<<tot<<'\n';
			return;
		}
		int s1=ans,s2=ans,s3=ans;
		int now=n-14*ans;
		while(now)
		{
			if(pre[now]==now-3) ++s1;
			if(pre[now]==now-4) ++s2;
			if(pre[now]==now-7) ++s3;
			now=pre[now];
		}
		cout<<s3<<' '<<s2<<' '<<s1<<'\n';
	}
}
signed main()
{
	red::main();
	return 0;
}

跑步

题意:

\(n\)拆分成若干个数字之和,有多少种不同的方案数?

\(n\leq 10^5\)

有一个暴力\(dp\)

我们假设后面的数字都比前面小

\(dp[i][j]\)表示\(i\)个数字,和为\(j\)的方案数。

\(dp[i][j]=dp[i-1][j-1]+dp[i][j-i]\)

等号右边,前者表示加一个数字\(1\),后者表示之前所有数字\(+1\)

复杂度\(O(n^2)\)

根号分治。

当数字小于等于根号\(n\)时,做一个完全背包,复杂度\(O(n\sqrt{n})\)

当数字大于根号\(n\)时,数字最多有\(\sqrt{n}\)个,沿用上面的方法,复杂度是\(O(n\sqrt{n})\)

再把答案拼起来。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define lowbit(i) ((i)&(-i))
	const int N=1e5+10;
	int n,mod;
	int f[N],g[350][100010],dp[N];
	inline void main()
	{
		cin>>n>>mod;
		int cnt=sqrt(n);
		f[0]=1;
		for(int i=1;i<=cnt;++i)
		{
			for(int j=i;j<=n;++j)
			{
				f[j]=(f[j]+f[j-i])%mod;
			}
		}
		g[0][0]=1;
		for(int i=1;i<=n/cnt+1;++i)
		{
			for(int j=cnt+1;j<=n;++j)
			{
				g[i][j]=(g[i-1][j-(cnt+1)]+g[i][j-i])%mod;
			}
		}
		for(int j=0;j<=n;++j)
		{
			for(int i=0;i<=n/cnt+1;++i) dp[j]=(dp[j]+g[i][j])%mod;
		}
		
		int ans=0;
		for(int i=0;i<=n;++i) ans=(ans+f[i]*dp[n-i])%mod;
		cout<<ans<<'\n';
	}
}
signed main()
{
	red::main();
	return 0;
}
/*
dp[i][j]=\sum_k=1^j{dp[i-k][k]}
*/

魔法

题意:

给一个\(n\)个点,\(m\)条边的有向图,有\(k\)次机会把一条边的边权变成负的,求从\(1\)\(n\)的最小代价。

\(n\leq 100,m\leq 2500,k\leq 10^6\)

\(k=0\)时特判掉,直接跑\(spfa\)

\(k\leq 1000\)时,可以分层图最短路

\(k\leq 10^6\)时:

预处理\(dp[i][j]\)表示从\(i\)走到\(j\),最多用一次魔法的最小代价。

然后把这个矩阵自乘\(k\)次,用快速幂加速。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define lowbit(i) ((i)&(-i))
	const int N=100+10,inf=2e15;
	int n,m,k;
	typedef pair<int,int> pr;
	vector<pr> eg[N];
	int f[N][N];
	int dis[N][2];
	bool vis[N][2];
	struct node
	{
		int x,y,dis;
		inline bool operator < (const node &t) const
		{
			return dis>t.dis;
		}
	};
	struct matrix
	{
		int a[N][N];
	}T,ret;
	priority_queue<node> q;
	inline matrix mul(matrix a,matrix b)
	{
		matrix c;
		for(int i=1;i<=n;++i)
		{
			for(int j=1;j<=n;++j) c.a[i][j]=inf;
		}
		for(int i=1;i<=n;++i)
		{
			for(int j=1;j<=n;++j)
			{
				for(int k=1;k<=n;++k)
				{
					c.a[i][j]=min(c.a[i][j],a.a[i][k]+b.a[k][j]);
				}
			}
		}
		return c;
	}
	inline matrix fast(matrix x,int k)
	{
		matrix ret;
		for(int i=1;i<=n;++i)
		{
			for(int j=1;j<=n;++j)
			{
				if(i==j) ret.a[i][j]=0;
				else ret.a[i][j]=inf;
			}
		}
		while(k)
		{
			if(k&1) ret=mul(ret,x);
			x=mul(x,x);
			k>>=1;
		}
		return ret;
	}
	inline void spfa(int st)
	{
		q.push((node){st,0,0});
		memset(dis,0x3f,sizeof(dis));
		memset(vis,0,sizeof(vis));
		dis[st][0]=0;
		while(!q.empty())
		{
			int now=q.top().x,b=q.top().y;
			q.pop();
			vis[now][b]=0;
			for(pr tmp:eg[now])
			{
				int t=tmp.first,v=tmp.second;
				if(dis[t][b]>dis[now][b]+v)
				{
					dis[t][b]=dis[now][b]+v;
					if(!vis[t][b])
					{
						q.push((node){t,b,dis[t][b]});
						vis[t][b]=1;
					}
				}
				if(b==0)
				{
					if(dis[t][1]>dis[now][0]-v)
					{
						dis[t][1]=dis[now][0]-v;
						if(!vis[t][1])
						{
							vis[t][1]=1;
							q.push((node){t,1,dis[t][1]});
						}
					}
				}
			}
		}
	}
	inline void main()
	{
		ios::sync_with_stdio(0);
		cin.tie(0),cout.tie(0);
		cin>>n>>m>>k;
		memset(f,0x3f,sizeof(f));
		for(int i=1;i<=m;++i)
		{
			int x,y,z;
			cin>>x>>y>>z;
			eg[x].emplace_back(pr(y,z));
		}
		if(!k)
		{
			spfa(1);
			cout<<dis[n][0]<<'\n';
			return;
		}
		for(int i=1;i<=n;++i)
		{
			spfa(i);
			for(int j=1;j<=n;++j) T.a[i][j]=min(dis[j][0],dis[j][1]);
		}
		for(int i=1;i<=n;++i)
		{
			for(int j=1;j<=n;++j)
			{
				T.a[i][j]=min(T.a[i][j],inf);
			}
		}
		matrix ans=fast(T,k);
		cout<<ans.a[1][n]<<'\n';
	}
}
signed main()
{
	red::main();
	return 0;
}
/*
4 3 0
1 2 5
2 3 4
3 4 1

*/
posted @ 2022-05-11 19:09  lovelyred  阅读(33)  评论(0编辑  收藏  举报