A. 打怪
先求出每次打死一只怪需要掉多少血,然后就直接算出能够打死多少只。
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
IO;
int T=1;
cin>>T;
while(T--)
{
int h,a,H,A;
cin>>h>>a>>H>>A;
int cnt=(H+a-1)/a;
int p=(cnt-1)*A;
if(p) cout<<(h-1)/p<<'\n';
else cout<<"-1\n";
}
return 0;
}
B. 吃水果
不妨设 n < m n<m n<m,如果我们先吃 k k k次然后加倍一次能够使得 m = n m=n m=n那么需要满足 2 × ( n − k ) = m − k 2×(n-k)=m-k 2×(n−k)=m−k,得出 k = 2 n − m k=2n-m k=2n−m,由此只需先让加倍到 2 n > = m 2n>=m 2n>=m然后吃 k k k次再次加倍然后在吃 m − k m-k m−k次即可吃完,由此上面贪心一定最小。
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
IO;
int T=1;
cin>>T;
while(T--)
{
int n,m;
cin>>n>>m;
if(n>m) swap(n,m);
int res=m;
while(n<m)
{
res++;
n*=2;
}
cout<<res<<'\n';
}
return 0;
}
C. 四个选项
首先先用并查集统计出每个连通块的数量,然后把每一个连通块看成一个物品,跑一边背包问题即可。
状态表示: f ( i , j , k , l , r ) f_{(i,j,k,l,r)} f(i,j,k,l,r)考虑前 i i i个物品,答案A选了 j j j个,答案B选了 k k k个,答案C选了 l l l个,答案D选了 r r r个的集合。
状态转移:考虑第 i i i个物品放在哪个答案中不难写出转移。
感觉这方法有点暴力,但是思路应该还是非常好理解
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=15;
int p[N],sz[N];
ll f[N][N][N][N][N];
int find(int x)
{
return x==p[x]?x:p[x]=find(p[x]);
}
int main()
{
IO;
int T=1;
//cin>>T;
while(T--)
{
for(int i=1;i<=12;i++) p[i]=i,sz[i]=1;
int a,b,c,d,m;
cin>>a>>b>>c>>d>>m;
vector<int> v;
v.push_back(0);
while(m--)
{
int a,b;
cin>>a>>b;
int pa=find(a),pb=find(b);
if(pa==pb) continue;
sz[pb]+=sz[pa];
p[pa]=pb;
}
for(int i=1;i<=12;i++)
if(p[i]==i) v.push_back(sz[i]);
f[0][0][0][0][0]=1;
int n=v.size()-1;
for(int i=1;i<=n;i++)
for(int j=0;j<=a;j++)
for(int k=0;k<=b;k++)
for(int l=0;l<=c;l++)
for(int r=0;r<=d;r++)
{
if(j>=v[i]) f[i][j][k][l][r]+=f[i-1][j-v[i]][k][l][r];
if(k>=v[i]) f[i][j][k][l][r]+=f[i-1][j][k-v[i]][l][r];
if(l>=v[i]) f[i][j][k][l][r]+=f[i-1][j][k][l-v[i]][r];
if(r>=v[i]) f[i][j][k][l][r]+=f[i-1][j][k][l][r-v[i]];
}
cout<<f[n][a][b][c][d]<<'\n';
}
return 0;
}
D.最短路变短了
原图跑一边dijsktra算出dist1[i]
表示1~i
的最短距离,在跑一边反图算出dist2[i]
表示i~n
的最短距离。
考虑如果 u → v u\to v u→v之间的边取反能够缩短 1 → n 1\to n 1→n的最短距离,那么说明目前最短路径一定是 1 → ⋯ → v → u → ⋯ → n 1\to \dots \to v\to u\to \dots \to n 1→⋯→v→u→⋯→n,因此只需要判断该条件是否成立即可得到答案(dist1[v]+dist2[u]+c<dist1[n]
)
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair<ll,int> pli;
const int N=100010,M=400010;
int h1[N],h2[N],e[M],ne[M],idx;
ll w[M];
ll dist1[N],dist2[N];
bool st[N];
struct node
{
int u,v;
ll w;
}E[M];
int n,m,q;
void add(int h[],int a,int b,ll c)
{
e[idx]=b;
ne[idx]=h[a];
w[idx]=c;
h[a]=idx++;
}
void dijkstra(int start,int h[],ll dist[])
{
memset(dist,0x3f,8*N);
memset(st,0,sizeof st);
priority_queue<pli,vector<pli>,greater<pli> >q;
dist[start]=0;
q.push({
0,start});
while(q.size())
{
int t=q.top().second;q.pop();
if(st[t]) continue;
st[t]=1;
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[t]+w[i])
{
dist[j]=dist[t]+w[i];
q.push({
dist[j],j});
}
}
}
}
int main()
{
IO;
int T=1;
//cin>>T;
while(T--)
{
memset(h1,-1,sizeof h1);
memset(h2,-1,sizeof h2);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>E[i].u>>E[i].v>>E[i].w;
add(h1,E[i].u,E[i].v,E[i].w);
add(h2,E[i].v,E[i].u,E[i].w);
}
dijkstra(1,h1,dist1);
dijkstra(n,h2,dist2);
cin>>q;
while(q--)
{
int id;
cin>>id;
int u=E[id].u,v=E[id].v,c=E[id].w;
if(dist1[n]>dist1[v]+dist2[u]+c) cout<<"YES\n";
else cout<<"NO\n";
}
}
return 0;
}
E.相似的子串
字符串哈希+二分
二分扫一遍序列即可,用哈希表记录出现次数已经最后一次出现的位置。
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
#include<unordered_map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int N=200010,P=131;
unordered_map<ull,pii> mp;
ull h[N],p[N];
int n,k;
char s[N];
ull get(int l,int r)
{
return h[r]-h[l-1]*p[r-l+1];
}
bool check(int mid)
{
mp.clear();
for(int i=mid;i<=n;i++)
{
ull v=get(i-mid+1,i);
if(mp[v].second<=i-mid)
{
mp[v].first++;
mp[v].second=i;
if(mp[v].first>=k) return 1;
}
}
return 0;
}
int main()
{
IO;
int T=1;
//cin>>T;
while(T--)
{
cin>>n>>k;
cin>>s+1;
p[0]=1;
for(int i=1;i<=n;i++)
{
p[i]=p[i-1]*P;
h[i]=h[i-1]*P+s[i]-'a'+1;
}
int l=0,r=n;
while(l<r)
{
int mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<l<<'\n';
}
return 0;
}
F.苹果树
点分治?待补?