“范式杯”2023牛客暑期多校训练营1 补题

D Chocolate  博弈论,结论

H Matches 线段树求最大交

J Roulette 数学,概率论

K Subdivision 图论 细节

M Water 扩展欧几里得

 

n=1&&m=1时W必胜

n=1或者m=1时先手必胜。

n>1&&m>1时,假设W必胜,则W第一次消除时一定不会选择(n,m),所以他第一次消除时一定会剩下一部分。而先手完全可以事先达到这一状态,故先手必胜

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 7;

int main()
{

    int n, m;
    cin>>n>>m;

    if(n==1&&m==1)
    {
        cout << "Walk Alone";
    }
    else
    {
        cout << "Kelin";
    }
    return 0;
}

 

 

 

只有如图一组i,j才能对答案产生贡献,贡献为线段交集的二倍。考虑将线段分组,上行与下行线段。都按照下端点从高到低进行排序。以访问上行 查询下行为例,每次把下端点大于等于本次下端点的下行线段加入线段树。线段树的下标是线段上端点的离散值。维护的内容有下端点的最小值与线段长度。我们求交集,只需要分为两类查询,一是上端点在本次上端点之内的,我们查询其长度最大值。上端点在本次上端点之外的,1我们查询其下端点最小值即可。对于查询到不相交的情况,贡献为正,故不会对答案产生影响。
 

#include <bits/stdc++.h>

using namespace std;
using ll = long long;
int a[1000000+10],b[1000000+10],n,len;
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9')
        x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int lisan[1000000*2+10];
struct node
{
    int down,up;
};
struct node s1[1000000+10],s2[1000000+10];
bool cmp(struct node x, struct node y)
{
    return x.down>y.down;
}
ll maxx[1000000*2*4+10];
ll down[1000000*2*4+10];

void build(int root,int l,int r)
{
    if(l==r)
    {
        maxx[root]=-1e15;
        down[root]=1e15;
        return;
    }
    int mid=(l+r)>>1;

    build(root<<1,l,mid);
    build(root<<1|1,mid+1,r);

    maxx[root]=max(maxx[root<<1],maxx[root<<1|1]);
    down[root]=min(down[root<<1],down[root<<1|1]);
    return ;
}

ll query(int root,int l,int r,int L,int R,int flag)
{
    if(L<=l&&r<=R)
    {
        if(flag==1)
            return maxx[root];

        return down[root];
    }
    if(flag==1)
    {
        int mid=(l+r)>>1;
        ll nowmax=-1e15;

        if(L<=mid)
            nowmax=max(nowmax,query(root<<1,l,mid,L,R,flag));
        if(R>mid)
            nowmax=max(nowmax,query(root<<1|1,mid+1,r,L,R,flag));
        return nowmax;
    }
    else
    {
        int mid=(l+r)>>1;
        ll nowmax=1e15;

        if(L<=mid)
            nowmax=min(nowmax,query(root<<1,l,mid,L,R,flag));
        if(R>mid)
            nowmax=min(nowmax,query(root<<1|1,mid+1,r,L,R,flag));
        return nowmax;
    }

}
void change(int root,int l,int r,int pos,ll nowmaxx,ll nowdown)
{
    if(l==r)
    {
        maxx[root]=max(maxx[root],nowmaxx);

        down[root]=min(down[root],nowdown);

        return ;
    }
    int mid=(l+r)>>1;

    if(pos<=mid)
        change(root<<1,l,mid,pos,nowmaxx,nowdown);
    else
        change(root<<1|1,mid+1,r,pos,nowmaxx,nowdown);

    maxx[root]=max(maxx[root<<1],maxx[root<<1|1]);
    down[root]=min(down[root<<1],down[root<<1|1]);
}

int main()
{
  cin>>n;

  ll nowsum=0;
  for(int i=1;i<=n;i++)
  {
      a[i]=read();
      len++;
      lisan[len]=a[i];

  }
  for(int i=1;i<=n;i++)
  {
      b[i]=read();
      len++;
      lisan[len]=b[i];

      nowsum+=abs(a[i]-b[i]);
  }

  sort(lisan+1,lisan+1+len);

  len=unique(lisan+1,lisan+1+len)-lisan-1;

  int len1=0,len2=0;

  for(int i=1;i<=n;i++)
  {
      if(a[i]<b[i])
      {
          len1++;
          s1[len1].down=a[i];
          s1[len1].up=b[i];
      }
      else if(b[i]<a[i])
      {
          len2++;
          s2[len2].down=b[i];
          s2[len2].up=a[i];
      }
  }

  sort(s1+1,s1+1+len1,cmp);
  sort(s2+1,s2+1+len2,cmp);



  if(len2==0)
  {
      cout<<nowsum;
  }
  else
  {

      build(1,1,len);
      ll ans=nowsum;
      int l=1,r=0;
      for(int i=1;i<=len2;i++)
      {
          while(r+1<=len1&&s1[r+1].down>=s2[i].down)
          {
              int nowpos=lower_bound(lisan+1,lisan+1+len,s1[r+1].up)-lisan;

              change(1,1,len,nowpos,s1[r+1].up-s1[r+1].down,s1[r+1].down);
              r++;
          }

          int nowpos=lower_bound(lisan+1,lisan+1+len,s2[i].up)-lisan;

          ll nowmax=-1e15;

          nowmax=query(1,1,len,1,nowpos,1);

          ll nowmin=1e15;

          nowmin=query(1,1,len,nowpos,len,2);

        ll temp1=nowsum-2ll*nowmax;

        ll temp2=nowsum-2ll*(s2[i].up-nowmin);

        ans=min(ans,temp1);
        ans=min(ans,temp2);


      }


       build(1,1,len);



      l=1,r=0;
      for(int i=1;i<=len1;i++)
      {
          while(r+1<=len2&&s2[r+1].down>=s1[i].down)
          {
              int nowpos=lower_bound(lisan+1,lisan+1+len,s2[r+1].up)-lisan;

              change(1,1,len,nowpos,s2[r+1].up-s2[r+1].down,s2[r+1].down);
              r++;
          }
          int nowpos=lower_bound(lisan+1,lisan+1+len,s1[i].up)-lisan;
          ll nowmax=-1e15;
          nowmax=query(1,1,len,1,nowpos,1);
          ll nowmin=1e15;
          nowmin=query(1,1,len,nowpos,len,2);
        ll temp1=nowsum-2ll*nowmax;
        ll temp2=nowsum-2ll*(s1[i].up-nowmin);
        ans=min(ans,temp1);
        ans=min(ans,temp2);

      }
      cout<<ans;
  }
    return 0;
}

 

直接胜一场,钱总数+1,而后投标为1

败一场后胜一场,钱先-1后+2,总数+1,之后投标为1

败两场后胜一场,钱先减3后加4,总数+1,之后投标为1

所以得出结论,最终要想到达n+m,必须经过m轮连败(可为0)后胜。

设当前钱数是r,连败cnt次 (2^cnt)-1<=r  故可以求出cnt

则当前胜一场的,是1减去全败的概率=1-(1/2)^r。 直接遍历m次肯定超时,但是,考虑到相同cnt对应r是有一个很大区间的,且指数增长。同一区间的钱数r相同,胜的概率更相同。所以可以分段考虑。

 

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const ll N = 998244353;

ll qw(ll x, ll y)
{
    ll res = 1;
    for (; y; y >>= 1, x = (x * x )% N)
    {
        if (y & 1)
        {
            res = (res * x) % N;
        }
    }
    return res;
}

ll inv(ll x)
{
    return qw(x, N - 2);
}
ll LOG2(ll x)
{
    int ans=0;

    while((1ll<<ans)-1<x)
    {
        ans++;
    }
    if((1ll<<ans)-1==x)
        return ans;
    ans--;
    return ans;
}

int main()
{
    int n, m;
    cin >> n >> m;
    ll ans = 1;
    for (int i = n ; i <= n + m-1; )
    {
        int x = LOG2(i);//当前来说,如果要达到i,r不能超过这个
        ll v = inv(qw(2,x))*(qw(2,x)-1) % N;//成功的概率
        int num = min(m + n, (1 << (x + 1)) - 1);  //从i到num,r都是相同的,
        ans = (ans * qw(v, num - i) )% N;
        i = num;
    }
    cout << ans;
    return 0;

}

 

边权为1是本题突破点,意味着我们可以直接求出最短路上的边。先跑出BFS最短路,如果去影响最短路<=k的点。只有在一条路线末端没有其他点引出,且末端<k,这样在末端加即可。

否则,影响无法度量。考虑到,最短路<k的点,如果有引出的边,在这些边上随便加,对任何旧点最短路都没要任何影响,故考虑能加就加。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 7;

typedef struct
{
    int b, e;
} xinxi;
xinxi s[200000 * 2 + 10];
int f[200000 * 2 + 10], nex[200000 * 2 + 10], len = 2;
int vis[200000 * 2 + 10];
int book[200000 + 10], dis[200000 + 10];
void add(int x, int y)
{
    s[len].b = x;
    s[len].e = y;
    nex[len] = f[x];
    f[x] = len;
    len++;
}
int x[200000 + 10], y[200000 + 10];
int du[200000 + 10];
int main()
{

    memset(f, -1, sizeof(f));
    int n, m, k;
    cin >> n >> m >> k;

    for (int i = 1; i <= m; i++)
    {
        scanf("%d%d", &x[i], &y[i]);
        add(x[i], y[i]);
        add(y[i], x[i]);
        du[x[i]]++;
        du[y[i]]++;
    }

    queue<int> q;

    for (int i = 1; i <= n; i++)
    {
        dis[i] = 2e9;
    }

    dis[1] = 0;

    q.push(1);

    book[1] = 1;

    while (!q.empty())
    {
        int now = q.front();
        q.pop();
        int x = f[now];

        while (x != -1)
        {
            int j = s[x].e;

            if (book[j])
            {
                x = nex[x];
                continue;
            }

            book[j] = 1;
            dis[j] = dis[now] + 1;
            vis[x] = vis[x ^ 1] = 1;
            q.push(j);

            x = nex[x];
        }
    }

    ll ans = 0;

    for (int i = 1; i <= n; i++)
    {
        if (dis[i] <= k)
            ans++;
    }
    for (int i = 1; i <= m; i++)
    {
        int id = (i - 1) * 2 + 2;

        //  cout << vis[id] << " ";
        if (vis[id])
        {
            continue;
        }

        if (dis[x[i]] < k)
        {

            ans += (k - dis[x[i]]);
        }

        if (dis[y[i]] < k)
        {
            ans += (k - dis[y[i]]);
        }
    }

    for (int i = 2; i <= n; i++)
    {
        if (du[i] == 1 && dis[i] < k)
        {
            ans += (k - dis[i]);
        }
    }
    cout << ans;
    return 0;
}

 

 

 

 数学题,扩欧,付队友皮哥代码

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if (!b)
    {
        x = 1, y = 0;
        return a;
    }
    ll res = exgcd(b, a % b, y, x);
    y -= x * (a / b);
    return res;
}
ll calc(ll a, ll b)
{
    ll res = (abs(a) * 2 + abs(b) * 2);
    if (a < 0 || b < 0)
        res--;
    return res;
}
int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t--)
    {
        ll A, B, k;
        cin >> A >> B >> k;
        if (A < B)
            swap(A, B);
        ll x, y;
        ll gcd = exgcd(A, B, x, y);
        if (k % gcd != 0)
        {
            cout << "-1\n";
            continue;
        }
        x *= k / gcd, y *= k / gcd;
        // cout << x << " " << y << "\n";
        ll ta = A / gcd, tb = B / gcd;
        // cout << k << " ";
        ll x1 = (x % tb + tb) % tb, y1 = (k - x1 * A) / B;
        ll x2 = x1 - tb, y2 = y1 + ta;
        ll y3 = (y % ta + ta) % ta, x3 = (k - y3 * B) / A;
        ll y4 = y3 - ta, x4 = x3 + tb;
        ll ans = 1e18;
        ll res1 = calc(x1, y1), res2 = calc(x2, y2), res3 = calc(x3, y3), res4 = calc(x4, y4);
        // cout << x1 << " " << y1 << "\n";
        // cout << x2 << " " << y2 << "\n";
        // cout << x3 << " " << y3 << "\n";
        // cout << x4 << " " << y4 << "\n";
        ans = min(ans, res1);
        ans = min(ans, res2);
        ans = min(ans, res3);
        ans = min(ans, res4);
        if (ans != 1e18)
            cout << ans << "\n";
        else
            cout << "-1\n";
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qinsanma and Code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值