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 待补