A-打怪
因为我先手,我的攻击力如果大于怪物的血量,那么我就能杀无数个,输出-1即可
考虑到我先手,所以先把怪物的血扣掉一次,然后就是怪物先手了,
计算出我的血量能够让怪物打我几次,以及我需要打几下怪物才能死即可。
#include <bits/stdc++.h> using namespace std; typedef long long ll; int main() { int t; cin >> t; while (t--) { int a, b, x, y; cin >> a >> b >> x >> y; if (b >= x) cout << "-1" << endl; else { int num = 0; int q = a / y; if (a % y) q++; x -= b; int p = x / b; if (x % b) p++; cout <<q/p+(q%p==0?-1:0)<< endl; } } return 0; }
当然了,数据量很小,模拟过程也可以
#include <bits/stdc++.h> using namespace std; typedef long long ll; int main() { int t; cin >> t; while (t--) { int a, b, x, y; cin >> a >> b >> x >> y; if (b >= x) cout << "-1" << endl; else { int num = 0; int flag = 0; int k = x; while (1) { if (flag == 0) { k -= b; if (k <= 0) num++, k = x; else flag = 1; } else { a -= y; if(a<=0) break; flag = 0; } } cout << num << endl; } } return 0; }
B-吃水果
计算n和m的关系是不是二倍
假设n小于m
如果是n * 2==m 自然是把n乘2,然后每天都吃一个,答案就是1+m;
如果n * 2>m 我们想让他们是二倍关系 设x天后满足二倍关系 则有 2 * (n-x)= m-x -> x=2*n-m
即花x天使得n * 2 = m 然后花一天把n * 2 因为m前面减少了x个 所以剩下m-x个 答案就是x+1+m-x =1+m
发现n * 2 >= m 答案都是m+1
如果n * 2<m 一直把n乘2,直到n * 2 >=m 即可
#include <bits/stdc++.h> using namespace std; typedef long long ll; int main() { int t; cin >> t; while (t--) { int n,m;cin>>n>>m; if(n==m) cout<<n<<endl; else { int sum=0; int mi=min(n,m),ma=max(n,m); while(mi*2<ma) mi<<=1,sum++; cout<<sum+1+ma<<endl; } } return 0; }
C-四个选项
范围很小,考虑暴搜。
用并查集合并,计算出来每个联通块的大小。搜每个块的即可 复杂度4^cnt cnt为块的个数
实际复杂度远远小于4^cnt 因为块的大小和a、b、c、d个数的限制会减少很多没必要的搜索过程
#include<bits/stdc++.h> using namespace std; int f[15]; int a,b,c,d,m; int cnt,ans[15]; int sum; int vis[15]; int find(int x){ return f[x]==x?x:f[x]=find(f[x]); } void join(int x,int y){ int fx=find(x),fy=find(y); if(fx!=fy) f[fx]=fy; } void dfs(int x){ if(x>cnt) { sum++;return ; } if(ans[x]<=a) a-=ans[x],dfs(x+1),a+=ans[x]; if(ans[x]<=b) b-=ans[x],dfs(x+1),b+=ans[x]; if(ans[x]<=c) c-=ans[x],dfs(x+1),c+=ans[x]; if(ans[x]<=d) d-=ans[x],dfs(x+1),d+=ans[x]; } int main(){ cin>>a>>b>>c>>d>>m; for(int i=1;i<=12;i++) f[i]=i; while(m--){ int x,y;cin>>x>>y; join(x,y); } for(int i=1;i<=12;i++){ int fa=find(i); if(!vis[fa]) ans[++cnt]++,vis[fa]=cnt; else ans[vis[fa]]++; } dfs(1); cout<<sum<<endl; return 0; }
D-最短路变短了
考虑以1为源点跑一次dijkstra,然后把边反向过来以n为源点跑dijkstra
那么对于第i条边 x->y 边权为d
如果d1[y]+d+d2[x] < d1[n] 就是YES 否则就是NO
我们这样考虑,如果原图的最短路经过第i条边 也就是经过了x->y 那么按照上面的式子我们等于走了1->x->y +d 相当于走了两边这条边,因为没有负边权 显然是会>d1[n]的 也就是NO
如果原图没有经过x->y 那么最短路想要要变短只可能是 1->y->x->n这样走 因为最短路的路径压根没有走这条 关键路径是不变的
#include<bits/stdc++.h> using namespace std; typedef long long ll; struct node { ll to, cost; friend bool operator<(node a, node b) { return a.cost > b.cost; } }; struct nodee { ll x, y, d; } q[200005]; int n, m; struct dijkstra { ll d[100005]; ll e[200005], v[200005], ne[200005], h[200005], idx; bool vis[100005]; void Init() { memset(h, -1, sizeof h); for (int i = 1; i <= n; i++) d[i] = -1, vis[i] = 0; } void add(ll a, ll b, ll c) { e[idx] = b, v[idx] = c, ne[idx] = h[a], h[a] = idx++; } void dj(int x) { priority_queue<node> q; q.push({x, 0}); while (!q.empty()) { node now = q.top(); q.pop(); if (!vis[now.to]) { vis[now.to] = 1; d[now.to] = now.cost; for (int i = h[now.to]; i != -1; i = ne[i]) { node next; next.to = e[i]; next.cost = now.cost + v[i]; if (!vis[next.to]) q.push(next); } } } } } a, b; int main() { cin>>n>>m; a.Init();b.Init(); for (int i = 1; i <= m; i++) { ll x, y, d; cin >> x >> y >> d; q[i] = {x, y, d}; a.add(x, y, d); b.add(y, x, d); } a.dj(1),b.dj(n); int t; cin >> t; while (t--) { ll x;cin >> x; if (a.d[q[x].y] + q[x].d + b.d[q[x].x] >= a.d[n]) cout << "NO" << endl; else cout << "YES" << endl; } return 0; }
E
忽然发现这题挺中规中矩的,没做出来不应该(好吧 摊牌了我承认是我菜)。
题意要求k个有最长公共前缀长度为x且互不相交的x最大值,那么我们只需要考虑前缀即可,这样能保证答案最大。
可以明显发现,如果当前长度满足,那么我们肯定考虑长度大一点满不满足,如果当前长度不满足,那我们肯定考虑长度小一点满不满足。
所以问题可以转为二分答案来进行判定。
等于是要求最大的x,使得有k个长度为x的子串不相交,因为牵扯到求子串的个数有多少个,考虑哈希,O(n)预处理,然后可以O(1)求出任意子串的哈希值。
那么对于二分的判定,我们枚举每个子串,记录下这个子串的个数和出现的最后位置,现在的结尾位置减去记录下的结尾位置>=x 说明两个串不相交 满足不相交的话,我们可以把这个子串个数增加,并更新最后位置为当前的结尾位置 如果存在一个子串的个数≥k,说明长度x是满足的
这样的话,我们对于每次判定可以考虑一个umap的键来保存子串的哈希值,umap的值套一个pair,一个保存最后出现位置,一个保存子串个数
#include<bits/stdc++.h> using namespace std; typedef unsigned long long ull; unordered_map<ull,pair<int,int>> mp; const int maxn=2e5+5; int base=131; char s[maxn]; ull po[maxn],has[maxn]; int n,k; bool check(int x){ mp.clear(); for(int i=x;i<=n;i++){ ull ha=has[i]-has[i-x]*po[x]; if(i-mp[ha].first>=x) mp[ha].first=i,mp[ha].second++; if(mp[ha].second>=k) return 1; } return 0; } int main(){ cin>>n>>k>>s+1; po[0]=1; for(int i=1;i<=n;i++){ has[i]=has[i-1]*base+s[i]-'a'; po[i]=po[i-1]*base; } int l=0,r=n+1,ans=0; while(l+1<r){ int mid=l+r>>1; if(check(mid)) l=mid,ans=mid; else r=mid; } cout<<ans<<endl; return 0; }
F 待补