近几天的牛客竞赛补题

目录

1,氧气少年的lcm

 2.数组段数

3.氧气少年的水滴

4.数组操作 

5.天平 

6.树形dp 


 氧气少年的lcm

1,氧气少年的lcm

这道题,牛客难度为3,主要考察二进制分解和gcd,lcm的求法

这道题有两种操作

1.任选集合中两数,向集合插入两者的gcd(两数可以相等但不能相同)

2.任选集合中两数,向集合插入两者的和(两数可以相等,但不能相同)

思路:

因为要获得lcm,而lcm是gcd的整数倍,所以令t=lcm(x,y)/gcd(x,y).题目转化为获得t*gcd(x,y)

步骤:

1,先用两次操作1,插入两个gcd(x,y).

2.再持续连用两次操作2,加入两个2*gcd(x,y),两个4*gcd(x,y).......直至获得小于t的最大二进制数

3.由于我们获得了小于t的最大的二进制数,也就是t的最高位1代表的数,后面还有数,故我们对后面的数进行二进制分解.例如3=1+2.13=1+2+4+8.由此获得我们想要的t后面的尾巴.举个例子t=30=16+14.16是我们之前求得的小于t的最大二进制数,而14是我们要求的尾巴,14=2+4+8,而2,4,8我们之前就加入了我们可依次获得6,14.

#include//求得t
using namespace std;
#define int long long
struct node
{
    int op;
    int a,b;
};
int gcd(int x,int y)
{
    if(y==0)return x;
    return gcd(y,x%y);
}
void solve()
{
    int x,y;
    cin>>x>>y;
    int g=gcd(x,y);
    int lcm=x*y/g;
    if(x==lcm||y==lcm)
    {
        cout<<0<<"\n";
        return;
    }
    vectorv;
    int t=lcm/g;    
    v.push_back({1,x,y});
    v.push_back({1,x,y});
    int mx=1;
    while(mx*2>T;
    while (T--)
    {
       solve();
    }
    return 0;
}

 2.数组段数

数组段数

本题定义了一个东西,叫数组段数(链接:
最小的 kkk 使得将数组划分成连续的 kkk 段后,每个元素都属于某一段,且每段数组中的元素种类应该全部相同)

思路就是,从左端点开始记录每个点的段数,举个例子

元素        :2        2         3         1         3         1

段数        1         1         2         3         4         5

容易发现,当前元素可以先继承前面元素的段数,如果与前面元素不同,就递增1,因此,数组段数是相对位置而言的,所以用他们的差值求数组段数毫无问题,如区间[2,4]=3-1+1=3.注意不要每次查询都重新计算数组段数,会超时

#include
using namespace std;
#define int long long
signed main()
{
  ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
  int n,m;
  cin>>n>>m;
  vectorv(n+5);
  vectorans(n+5);
  ans[1]=1;
  for(int i=1;i<=n;i++)
  {
  	cin>>v[i];
  }
  for(int i=2;i<=n;i++)
  {
  	ans[i]=ans[i-1];
  	if(v[i]!=v[i-1])
  	ans[i]++;
  }
  while(m--)
  {
  	int l,r;
  	cin>>l>>r;
	cout<

氧气少年的水滴

3.氧气少年的水滴

这个题目有意思,考察了双指针的应用

用l记录左边接收水滴的点,用r记录右边接收水滴的点,直至l<1剩下往左飞的水滴会飞向无穷,r>size,剩下往右飞的水滴会飞向无穷用lnum记录往左飞的水滴数,用rnum记录往右的水滴数,还有这题适合用do while循环,因为引爆第一个水滴后,总要执行一次操作

#include
using namespace std;
#define int long long
const int maxn=2e5+5;
int a[maxn];
signed main()
{
    int t;
    cin>>t;
    while(t--)
    {
      int n,p;
      cin>>n>>p;
      for(int i=1;i<=n;i++)cin>>a[i];
      if(a[p]!=9)
      {
        cout<<0<<" "<<0<<"\n";
        continue;
      }
      int l=p-1,r=p+1,lnum=1,rnum=1;
      do{
        while(a[l]+lnum>=10&&l>=1)
        {
            lnum-=10-a[l];
            lnum++;rnum++;
            l--;
        }
        while(a[r]+rnum>=10&&r<=n)
        {
            rnum-=10-a[r];//接收水滴
            rnum++;lnum++;
            r++;
        }
      }while((l>=1&&a[l]+lnum>=10)||(r<=n&&a[r]+rnum>=10));
      if(l>=1)lnum=0;
      if(r<=n)rnum=0;
      cout<

数组操作

4.数组操作 


小红拿到了两个正整数x,y,她可以进行以下两种操作:
1. 将两个数同时乘以aaa。
2. 若aaa既是xxx的因子,也是yyy的因子,则将两个数同时除以aaa。
小红希望最终两个数都在[l,r][l,r][l,r]区间内。她想知道最终的xxx有多少种不同的取值方案?

这到底要求最终的x的个数,先求gcd(x,y),再将x,y都除以gcd.再以此为基础算个数,

题目转化可成乘数有多少个,x的乘数区间是[l/x,r/x],y的乘数区间是[l/y,r/y].求两个区间的交集即可

注意求左区间时,l%x为真时要向上取整如x=3,l=4,l/x=1此时1显然不能让x>l,如果x=4,l=4,左区间取1刚刚好,右区间都向下取整即可,因为不能超过右区间.

#include
using namespace std;
#define int long long
int gcd(int x,int y)
{
    if(y==0)return x;
    return gcd(y,x%y);
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int x,y,l,r;
    cin>>x>>y>>l>>r;
    int divide=gcd(x,y);
    x/=divide;
    y/=divide;
    int l1=l%x?l/x+1:l/x,l2=l%y?l/y+1:l/y;
    int ll=max(l1,l2);
    int rr=min(r/x,r/y);
    cout<

天平

5.天平 

这个题的原理就是裴蜀定理ax+by=gcd(a,b)一定有解,结论可以扩展到n个整数x,y可以为负数,正负代表放在天平的不同端

所以只要判断m是否是gcd(a,b,c)的倍数即可

#include
using namespace std;
int gcd(int x,int y)
{
    if(y==0)return x;
    return gcd(y,x%y);
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int a,b,c,m;
        cin>>a>>b>>c>>m;
        if(m%gcd(a,gcd(b,c))==0)
            cout<<"YES"<<"\n";
        else
            cout<<"NO"<<"\n";
    }
    return 0;
}

树形dp

6.树形dp 

树形dp板子(其实我不太会,但听别人说这个很重要)

//树形dp
#include
using namespace std;
#define int long long
vectorg[202020];
int mod=1e9+7;
int dp[101010][2];//dp[i][0]代表i节点不染红i号子树的方案数,dp[i][1]代表节点染红
void dfs(int x,int pr)
{
    dp[x][0]=dp[x][1]=1;//初始化
    for(auto i:g[x])
    {
        if(i==pr)continue;
        dfs(i,x);
        dp[x][0]=dp[x][0]*dp[i][1]%mod;//根节点不染色,孩子都要染
        dp[x][1]=dp[x][1]*(dp[i][0]+dp[i][1])%mod;//根节点染色,孩子染不染均可
    }
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int n;
    cin>>n;
    for(int i=1;i>x>>y;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    dfs(1,0);//默认以1为根,从1开始搜索
    cout<<(dp[1][0]+dp[1][1])%mod;
    return 0;
}

你可能感兴趣的:(算法,动态规划)