首页 > 其他分享 >AtCoder Beginner Contest 246

AtCoder Beginner Contest 246

时间:2023-03-29 19:33:20浏览次数:68  
标签:AtCoder matrix Beginner int res long 246 include dp

AtCoder Beginner Contest 246

A (思维)

A

这个题大意是告诉你一个矩形的三个点,求第四个点,并且已知每条边都是平行于\(x\)轴或者是\(y\)轴的,那么我们可以确定,\(x\)坐标只有两种,并且每一种都有两个,\(y\)坐标也是

题目输入三个坐标,那么答案就是缺少的那个个(数量为\(1\)的那一个坐标)

所以我们可以用异或来处理

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include<cmath>
#include <unordered_map>
#include <array>
#include <cstring>
using namespace std;
#define int long long 
#define ios  ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define inf 1e18
#define INF 1e18
const int maxn=2e5+10;
const int mod=998244353;
int t;
void solve()
{
    int x,y;
    cin>>x>>y;
    for (int i=1;i<=2;i++)
    {
        int tx,ty;
        cin>>tx>>ty;
        x^=tx;
        y^=ty;
    }
    cout<<x<<" "<<y<<"\n";
    return ;
}
signed main ()
{
  //  cin>>t;
  t=1;
    while (t--)
    {
        solve();
    }
    system ("pause");
    return 0;
}

B(向量,几何)

B

这道题在\(vp\)的时候死活没看懂,我也是看了其他人的解释才知道这个题的题意

大意就是给你一个点的坐标\(x\)和\(y\),我们可以把从原点到这个点的方向移动,找到到距离为\(1\)的位置并输出,同一个方向,距离为\(1\),不就很像向量里面的方向向量

所以我们可以用求方向向量的方法来求,故直接除以模

记得数据的类型为\(double\)

#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <string>
#include <queue>
#include <cmath>
using namespace std;
const int maxn=1500+10;
#define int long long 
signed main ()
{
  double x,y;
  cin>>x>>y;
  double sx,sy;
  double l=sqrt(x*x+y*y);
  sx=x/l;
  sy=y/l;
  printf ("%.10lf %.10lf\n",sx,sy);
   system ("pause");
   return 0;
}

C(贪心)

C

这个题目大意就是有\(n\)个物品,有\(k\)张优惠券,每张优惠券最多可以省\(x\)元(当物品的价格小于\(x\)时,我们需要支付的费用为\(0\))

对于这些优惠券,我们要尽可能的让它发挥到最大效用。所以首先我们考虑每一张优惠券都帮我们省了\(x\)元,然后如果还有多余的优惠券,我们再考虑给那些价格高的使用

#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
#define int long long 
int n,k,x;
signed main ()
{
   cin>>n>>k>>x;
   vector<int>res;
   int sum=0;
   for (int i=1;i<=n;i++)
   {
      int t;
      cin>>t;
      int p=t/x;
      int get=0;
      if(k>=p)
      {
         get=p*x;
         k-=p;
      }
      else 
      {
         get=k*x;
         k=0;
      }
      sum+=(t-get);
      res.push_back(t-get);
   }
   sort(res.rbegin(),res.rend());
   for (auto x:res)
   {
      if(k)
      {
         sum-=x;
         k--;
      }
      else break;
   }
   cout<<sum<<"\n";
   system ("pause");
   return 0;
}

D (二分)

D

这个题是给你一个\(n\),问是否找到一对\(a\)和\(b\),满足\(x=a^3+b^3+a^2b+ab^2\),其中\(x\)是大于\(n\)的数中最小的一个

对于这一道题,\(n\)的范围是\(1e18\),故我们可以知道这个\(a\)和\(b\)的最大值是\(1e6\),所以我们不太可能一一去枚举\(a\)和\(b\),但是我们可以先枚举\(a\),然后对于\(b\)的选取采用二分的方式

#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <string>
#include <queue>
#include <cmath>
using namespace std;
const int maxn=1500+10;
#define int long long 
int mx=1e6;
int n;
int check(int a,int b)
{
   int res=a*a*a+b*b*b+a*a*b+a*b*b;
   return res;
}
signed main ()
{
   cin>>n;
   int ans=1e18+10;
   for (int a=0;a<=mx;a++)
   {
      int l=0,r=mx;
      int t=mx;
      while (l<=r)
      {
         int mid=(l+r)>>1;
         int p=check(a,mid);
         if(p>=n)
         {
            ans=min(ans,p);
            r=mid-1;
         }
         else 
         {
            l=mid+1;
         }
      }
   }
   cout<<ans<<"\n";
   system ("pause");
   return 0;
}

E (bfs)

E

题目给出一个网格,一个起点,一个终点,其中\(.\)代表空地,可以走,\(#\)代表障碍,不可以走,其中走的方式也和一般的走法不同,他是斜着走的

比如此时为\((x,y)\),它有以下四种走法

走到\((x+d,y+d)\),但要保证\((x+1,y+1)\),\((x+2,y+2)\),\((x+3,y+3)\),直到\((x+d,y+d)\)这些点都是空地

走到\((x+d,y-d)\),但要保证\((x+1,y-1)\),\((x+2,y-2)\),\((x+3,y-3)\),直到\((x+d,y-d)\)这些点都是空地

走到\((x-d,y+d)\),但要保证\((x-1,y+1)\),\((x-2,y+2)\),\((x-3,y+3)\),直到\((x+d,y+d)\)这些点都是空地

走到\((x-d,y-d)\),但要保证\((x-1,y-1)\),\((x-2,y-2)\),\((x-3,y-3)\),直到\((x-d,y-d)\)这些点都是空地

问从起点走到终点最小需要走多少次

对于可不可以到达,如果两个点的坐标的和的奇偶性不同,那么不管如何都是到达不了的(每一次横纵坐标的变化和都是偶数)

这个很像最短路,但是有一点要注意,加入队列里面的不要太多了,不然会\(re\)

#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <string>
#include <queue>
using namespace std;
const int maxn=2500+10;
#define int long long 
int n;
int sx,sy,ex,ey;
string s[maxn];
bool vis[maxn][maxn];
int dx[4]={-1,-1,1,1};
int dy[4]={1,-1,1,-1};
struct node
{
   int x,y,dis;
   friend  bool operator < (const node a,const node b)
   {
      return a.dis>b.dis;
   }
};
bool check(int x,int y)
{
    if(x>=1&&y>=1&&x<=n&&y<=n)
    {
        if(s[x][y]=='.')
        {
            return true;
        }
    }
    return false;
}
int bfs(int sx,int sy)
{
   priority_queue<node>q;
   q.push({sx,sy,0});
   vis[sx][sy]=true;
   while (!q.empty())
   {
      auto [x,y,dis]=q.top();
      q.pop();
     // if(vis[x][y]) continue;//如果是在这儿判断,可能会有很多重复的坐标进入到队列里面,然后就会re,所以我们可以在进入队列的时候判断一下这个是否在队列里面
     // vis[x][y]=true;  
      if(x==ex&&y==ey) return dis;
      for (int d=0;d<4;d++)
      {
         int tx=x+dx[d];
         int ty=y+dy[d];
         while (check(tx,ty))
         {
            if(tx==ex&&ty==ey) return dis+1;
            if(!vis[tx][ty]) q.push({tx,ty,dis+1});
            vis[tx][ty]=true;
            tx+=dx[d];
            ty+=dy[d];
         }
      }
   }
   return -1;
}
signed main ()
{
   cin>>n>>sx>>sy>>ex>>ey;
   for (int i=1;i<=n;i++)
   {
      cin>>s[i];
      s[i]=" "+s[i];
   }
   if((sx+sy)%2==(ex+ey)%2)
   {
      cout<<bfs(sx,sy)<<"\n";
   }
   else 
   {
      cout<<-1<<"\n";
   }
   system ("pause");
   return 0;
}

F(容斥)

F

这个题大意是给你\(n\)个字符串,我们要从这\(n\)个字符串里面选择\(l\)次,每次选出一个字符串,然后从字符串里面选择出一个字符,组成一个长度为\(l\)的字符串,问最后有多少种不同的字符串

如果是只有一个字符串,那就很好办,直接计算出字母的种类\(cnt\),然后输出\(2^cnt\)

但是这个题不仅仅只有一个,还有其他的字符串和它一起构建字符串

所以我们就可以想到容斥

对于某一种搭配有偶数个组成的,用减法

对于某一种搭配有奇数个组成的,用加法

然后我们该怎么搭配呢

我们可以看到这个\(n\)最大只有\(18\),所以我们可以用二进制状态来表示

对于每一个搭配,都会有不同字母种类(所以在这些搭配里面的字母种类的叠加),对于重叠部分,只有是他们所有字符串都共有的

我们可以发现\(bitset\)可以很简单的实现以上效果,并快速求出所共有的字母\(cnt\),那么此时的组合方式有\(l^cnt\)个

#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <string>
#include <queue>
#include <bitset>
using namespace std;
const int maxn=2500+10;
#define int long long 
const int mod=998244353;
int n,l;
int ksm(int x,int p)
{
    int res=1;
    while (p)
    {
        if(p&1)
        {
            res=(res*x)%mod;
        }
        x=(x*x)%mod;
        p>>=1;
    }
    return res%mod;
}
signed main ()
{
    cin>>n>>l;
    bitset<36>state[20];
    for (int i=0;i<n;i++)
    {
        string s;
        cin>>s;
        for (int j=0;j<s.size();j++)
        {
            state[i][s[j]-'a']=1;
        }
    }
    int ans=0; 
    bitset<36>tmp;
    for (int now=1;now<(1<<n);now++)//从1开始,0会减去不该减的
    {
        tmp.set();
        int cnt=0;
        for (int j=0;j<n;j++)
        {
            if((now>>j)&1)
            {
                cnt++;
                tmp&=state[j];
            }
        }
        if(cnt&1)
        {
            ans=(ans+ksm(tmp.count(),l))%mod;
        }
        else 
        {
            ans=(ans-ksm(tmp.count(),l)+mod)%mod;
        }
    }
    cout<<ans<<"\n";
   system ("pause");
   return 0;
}

F(树形dp)

F

这个题大意是有一个数,小明要删除一个节点上的权值,小红从根节点出发,到它的子节点的位置,直到小红不可以走结束(叶子结点的位置),得分为小红所到达的位置的权值的最大值,小明想让小红的分数尽量低,小红想分数尽量高,小明是先手,问最后小红得到的最高分是多少

我们还是可以二分求解

我们二分小红的分数,然后我们进行一次\(dfs\),对于每一个点,只要它活着一个大于等于\(mid\)的权值,那么就可以了,那么对于小红来说,只要从根节点出发活着一个就可以成功的得到这个分数了

对于\(dfs\)里面怎么写

我们对于每一个点的子树的所有大于等于的数量都会记录,但是由于还会有小明来删除,并且小明需要先删除

所以,我们每一次这一个点的贡献都会\(res=max(res-1,0)+(w[i]>=mid)\)(小明需要删除的数量)

然后具体可以看代码

#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <string>
#include <queue>
#include <bitset>
using namespace std;
const int maxn=2e5+10;
#define int long long 
const int mod=998244353;
int n;
int w[maxn];
vector<int>g[maxn];
int dfs(int u,int fa,int now)
{
    int res=0;
    for (auto v:g[u])
    {
        if(v==fa) continue;
        res+=dfs(v,u,now);
    }
    res=max(res-1,0ll);
    if(w[u]>=now) res++;
    return res;
}
signed main ()
{
    cin>>n;
    int l=0,r=0;
    for (int i=2;i<=n;i++)
    {
        cin>>w[i];
        r=max(r,w[i]);
    }
    for (int i=2;i<=n;i++)
    {
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    int ans=0;
    while (l<=r)
    {
        int mid=(l+r)>>1;
        if(dfs(1,-1,mid))
        {
            ans=mid;
            l=mid+1;
        }
        else 
        {
           r=mid-1;
        }
    }
    cout<<ans<<"\n";
   system ("pause");
   return 0;
}

Ex(矩阵转移,dp,线段树)

Ex

题目很好懂,就是给你一个字符串,有\(0\)有\(1\)还有\(?\),每一次我们都会把\(?\)变成\(0\)或者\(1\),然后输出此时的\(01\)串的数量(其中\(?\)既可以变成\(0\),又可以变成\(1\),在查询\(01\)的数量的时候)

其实按照一般的思路来写,我们可以看成是一个\(dp\),\(dp[i] [0]\)代表以\(i\)为结尾,最后一个是\(0\)的数量,\(1\)同上

状态转移方程如下

如果\(s[i]=='0'\)

\[dp[i][0]=dp[i-1][1]+dp[i-1][0]+1 \]

\[dp[i][1]=dp[i-1][1] \]

如果\(s[i]=='1'\)

\[dp[i][1]=dp[i-1][1]+dp[i-1][0]+1 \]

\[dp[i][0]=dp[i-1][0] \]

如果\(s[i]=='?'\)

\[dp[i][1]=dp[i-1][1]+dp[i-1][0]+1 \]

\[dp[i][0]=dp[i-1][0]+dp[i-1][1]+1 \]

但是每一次我们都会修改\(?\)的状态,每一次都进行一次重新计算不太现实,然后有大佬使用了线段树和矩阵变换的方式

对于上面每一种字符,都会有不同的矩阵

我们可以构造一个下面这样的矩阵(因为最多有三项)

\[\left[ \begin{matrix} dp[i][0]\\ dp[i][1] \\ 1 \end{matrix} \right] \]

对于转移,我们可以构造\(A_{s_i}\)的矩阵

\[\left[ \begin{matrix} dp[i][0]\\ dp[i][1] \\ 1 \end{matrix} \right]=\left[ \begin{matrix} dp[i-1][0]\\ dp[i-1][1] \\ 1 \end{matrix} \right]\times A_{s_i} \]

然后根据矩阵的特性,我们可以发现

\[\left[ \begin{matrix} dp[n][0]\\ dp[n][1] \\ 1 \end{matrix} \right]=\left[ \begin{matrix} dp[1][0]\\ dp[1][1] \\ 1 \end{matrix} \right]\times A_{s_2}\times A_{s_2}\times A_{s_3}\times ...\times A_{s_n} \]

这样就很好计算了

如果\(s[i]=='0'\)

\[dp[i][0]=dp[i-1][1]+dp[i-1][0]+1 \]

\[dp[i][1]=dp[i-1][1] \]

可以变成这样

\[A_{s_i}=\left[\begin{matrix} 1 & 1 & 1\\ 0 & 1 & 0 \\ 0 & 0 & 0 \end{matrix}\right] \]

(如果不懂可以自己试试矩阵乘起来,感觉还蛮方便的)

如果\(s[i]=='1'\)

\[dp[i][1]=dp[i-1][1]+dp[i-1][0]+1 \]

\[dp[i][0]=dp[i-1][0] \]

可以变成这样

\[A_{s_i}=\left[\begin{matrix} 1&0&0\\ 1&1&1\\ 0&0&1 \end{matrix}\right] \]

如果\(s[i]=='?'\)

\[dp[i][1]=dp[i-1][1]+dp[i-1][0]+1 \]

\[dp[i][0]=dp[i-1][0]+dp[i-1][1]+1 \]

可以变成这样

\[A_{s_i}=\left[\begin{matrix} 1&1&1\\ 1&1&1\\ 0&0&1 \end{matrix}\right] \]

然后这里面\(n\)个矩阵的乘法有线段树来实现

具体的实现可以看代码

#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <string>
#include <queue>
#include <bitset>
using namespace std;
#define int long long 
#define lson root<<1
#define rson root<<1|1
const int mod=998244353;
const int inf=0x3f3f3f3f;
const int INF=1e9+7;
const int maxn=1e6+100;
string s;
struct segtree
{
	int l,r;
    int a[3][3];
}tr[maxn<<2];
int n,q;
void assign(int root,char ch)
{
     if(ch=='0')
    {
        tr[root].a[0][0] = 1;
        tr[root].a[0][1] = 1;
        tr[root].a[0][2] = 1;
        tr[root].a[1][0] = 0;
        tr[root].a[1][1] = 1;
        tr[root].a[1][2] = 0;
        tr[root].a[2][0] = 0;
        tr[root].a[2][1] = 0;
        tr[root].a[2][2] = 1;
    }
    else if(ch=='1')
    {
        tr[root].a[0][0] = 1;
        tr[root].a[0][1] = 0;
        tr[root].a[0][2] = 0;
        tr[root].a[1][0] = 1;
        tr[root].a[1][1] = 1;
        tr[root].a[1][2] = 1;
        tr[root].a[2][0] = 0;
        tr[root].a[2][1] = 0;
        tr[root].a[2][2] = 1;
    }
    else
    {
        tr[root].a[0][0] = 1;
        tr[root].a[0][1] = 1;
        tr[root].a[0][2] = 1;
        tr[root].a[1][0] = 1;
        tr[root].a[1][1] = 1;
        tr[root].a[1][2] = 1;
        tr[root].a[2][0] = 0;
        tr[root].a[2][1] = 0;
        tr[root].a[2][2] = 1;
    }
    return ;
}
void pushup(int root)
{
    for (int i=0;i<3;i++)
    {
        for(int j=0;j<3;j++)
        {
            tr[root].a[i][j]=0;
        }
    }
    for (int i=0;i<3;i++)
    {
        for(int j=0;j<3;j++)
        {
            for (int k=0;k<3;k++)
            {
                int x=tr[lson].a[i][k];
                int y=tr[rson].a[k][j];
                tr[root].a[i][j]=(x*y%mod+tr[root].a[i][j])%mod;
            }
        }
    }
    return ;
}
void build(int root,int l,int r)
{
    tr[root].l=l;
    tr[root].r=r;
    if (l==r) 
    {
        assign(root,s[r]);
        return ;
    }
    int mid=(l+r)>>1;
    build(lson,l,mid);
    build(rson,mid+1,r);
    pushup(root);
    return ;
}
void update(int root,int l,int r,char ch)
{
    if (l<=tr[root].l&&tr[root].r<=r)
    {
        assign(root,ch);
        return ;
    }
    int mid=(tr[root].l+tr[root].r)>>1;
    if (l<=mid)
    {
        update(lson,l,r,ch);
    }
    if (r>mid)
    {
        update(rson,l,r,ch);
    }
    pushup(root);
    return ;
}
signed main ()
{
    cin>>n>>q;
    cin>>s;
    s=" "+s;
    build(1,1,n);
    while (q--)
    {
        int x;
        char ch;
        cin>>x>>ch;
        update(1,x,x,ch);
        int ans=(tr[1].a[0][2]+tr[1].a[1][2])%mod;
        cout<<ans<<"\n";
    }
   system ("pause");
   return 0;
}

标签:AtCoder,matrix,Beginner,int,res,long,246,include,dp
From: https://www.cnblogs.com/righting/p/17270079.html

相关文章

  • AtCoder Beginner Contest 295
    A-ProbablyEnglish#include<bits/stdc++.h>usingnamespacestd;intread(){intx=0,f=1,ch=getchar();while((ch<'0'||ch>'9')&&ch......
  • Coinc1dens's lessons for cryptography beginner
    Coinc1dens'slessonsforcryptographybeginner10分题懒得写,赛后浅写一下(有些还真写不出来)太屑了古典懒得写,相信都写的出来1.childexgcdi即为m在模p情况下的乘法逆......
  • AtCoder Beginner Contest 145
    AtCoderBeginnerContest145https://atcoder.jp/contests/abc145D-Knight乍一看以为是dp,但是数据范围不允许。仔细一看发现,两种操作的次数是固定的,可以枚举出来每......
  • AtCoder Beginner Contest 148
    AtCoderBeginnerContest148https://atcoder.jp/contests/abc148这场比较简单D-BrickBreak二分orLIS#include<bits/stdc++.h>#definelllonglongusingn......
  • 【题解】Atcoder ABC295 A-G
    A.ProbablyEnglish题目分析:直接每一个单词判一下就好了。代码:点击查看代码#include<bits/stdc++.h>usingnamespacestd;intmain(){ intn;scanf("%d",&n); bo......
  • AtCoder Beginner Contest 248 F(连通性状压dp)
    F连通性状压dp思路看了dls的讲解后才明白一点点。状态\(dp[i][j][k]\)表示到表示到i列,删除了j条边,点i和n-1+i是否联通,对于下一列点,若当前i和n-1+i连通,则多出来的三条......
  • AtCoder Educational DP Contest
    1.A没什么难度,直接算就可以了。点击查看代码#include<bits/stdc++.h>#defineintlonglong#defineYesprintf("Yes\n")#defineNoprintf("No\n")#defineYESpr......
  • AtCoder Grand Contest 019 F Yes or No
    洛谷传送门AtCoder传送门首先容易发现最优策略是回答剩余多的选项。设\(n\)为剩余Yes的数量,\(m\)为剩余No的数量,考虑将\((n,m)\)放到平面上,若\(n>m\)则回......
  • AtCoder Grand Contest 008 F Black Radius
    洛谷传送门AtCoder传送门神题!!!!111考虑如何不重不漏地计数。先考虑全为1的情况,令\(f(u,d)\)为与\(u\)的距离\(\led\)的点集。首先单独算全集,那么对于不是全集......
  • AtCoder Beginner Contest 246
    AtCoderBeginnerContest246D题意求一个\(x\geqn\)使得\(x=a^3+a^2b+ab^2+b^3\)且\(n\leq10^{18}\)思路变形\(x=(a+b)(a^2+b^2)\),那么a、b的范围在1e6从大到小......