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
*/