ersgyegsesg

前言 :

  ATC质量感觉太好了,真的是见一题学一个新东西( 与其多大cf,不如老老实实的补高质量题。而ABC我觉得对我就是一个很好的选择,因此我打算经常去回来巩固在ABC做的题,就有了做这篇博客的想法。我目前水平太弱了,2200的题就要琢磨好久,为了提升自己,目标刷1200-2200的题。当然有些题可能是我认为是这样做,但是实际上相差百出,如果有大佬不经意看到此篇并给予建议与纠正,我会十分感激您的。

  我挺喜欢与各路OI/ACMer 交友的。

 

ABC 238

E Range Sums (板子题)

题意:一共有n个数,q次查询,每次告诉给你两个数(x,y),表示知道

ax+a(x+1)+...+a[y]的总和,问你根据q次询问后 是否能计算出a1+a2+...+an的总和

 

思路:这是一道差分约数的题,一般习惯性的add(x-1,y),add(y,x-1)这样建边。

sum[y]-sum[x-1]<=z
sum[y]-sum[x-1]>=z
也就约束成了,sum[y]-sum[x-1]=z

  

那么接下来我们只需要判断从1开始是否能连续的遍历到n 即可。

或者拿并查集维护也可。

AC代码:

map<int,int>mp;
void dfs(int x){
    for(int i=0;i<a[x].size();i++){
        if(!mp[a[x][i]]){
            mp[a[x][i]]=1;
            dfs(a[x][i]);
        }
    }
}
int main(){
    cin>>n>>q;
    for(int i=1;i<=q;i++){
        int v,u;
        cin>>v>>u;
        v--;
        a[v].push_back(u);
        a[u].push_back(v);
    }
    dfs(0);
    if(mp[n]){
        cout<<"Yes"<<endl;
    }else{
        cout<<"No"<<endl;
    }
}

 

F Two Exams

题意:n个学生,拥有两科成绩的排名。现在你可以自行安排人数为k个的队伍,但是如果未选中里的某个学生 两个排名都比选中队伍的任何一个学生的排名都低,则是不合法的。

思路:是一道线性dp的题,我们定义dp[当前i个学生][选了j个学生][未选中的学生的最低排名]

因此我们可以枚举一层 上一步未选中的学生第二个学科排名u以及选的学生人数

有两种状态

首先是选上当前学生,选上的前两维肯定存在这样的关系

dp(i-1)(j)----->dp(i)(j+1)

那么如果bi小于K的话,那么选中后未选中最小的一定是k,因此推出转移 dp[i][j+1][u]+=dp[i-1][j][u](如果b[i]<u)

如果不选的话,前二维是dp(i-1)(J)-->dp(i)(j)

那么未选中的学生就变成了min(b[i],u)

初始状态dp(0)(0)(n+1)=1

	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++)cin>>b[a[i]];
	f[0][0][n+1]=1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=k;j++){
			for(int u=0;u<=n+1;u++){
				if(b[i]<u){
					f[i][j+1][u]=(f[i][j+1][u] + f[i-1][j][u])%mod;
				}
				f[i][j][min(b[i],u)]=(f[i][j][min(b[i],u)] + f[i-1][j][u])%mod;
			}
		}
	}
	long long ans=0;
	for(int i=0;i<=n+1;i++) ans=(ans + f[n][k][i])%mod;

  这里有一个小技巧:就是如果我们读入bi是这样读入,那么我们在枚举的时候其实就是默认先将第一科排名进行了排序。

G - Cubic? (好题,需多看)

看了题解学会的一种hash查区间乘积为立方和的方法

给你N个数,最后有q个询问,让你判断 al-ar相乘的结果是否为立方数

这题可以用hash的做法 根据: a^b^(a^b)=0来判断出现次数是否为3的倍数

因此我们不关心a和b具体是多少,只要不相等都行

在一开始我们先进行一次经典的nlogn 求出一个数的所有的质因数

for(int i=2;i<MAX;i++){
          if(!v[i].empty()){
              continue;
          }
          for(int j=i;j<MAX;j+=i){
              int mj=j;
              while(mj%i==0){
                  mj/=i;
                  v[j].push_back(i);
              }
          }
      }

然后我们构造一个这样的hash方式:X0=random ,X1=random X2=X1^X2

原理就是 a^b^(a^b)=0 出现三次的话 就会异或成0,进而次数是3的倍数的话,异或最终一定是0

接着我们对每一个a[i]都进行这样的操作

Hi= Hi ^ X[a[i]某一个的质因子]【a[i]某一个质因子当前出现的次数%3】

总代码如下:

      uint_fast64_t seed=251902756251902756;
      mt19937_64 rand1(seed);
      vector<vector<int>>v(MAX);
      for(int i=2;i<MAX;i++){
          if(!v[i].empty()){
              continue;
          }
          for(int j=i;j<MAX;j+=i){
              int mj=j;
              while(mj%i==0){
                  mj/=i;
                  v[j].push_back(i);
              }
          }
      }
      vector<vector<long long>>x(MAX,vector<long long>(3,0));
      for(int i=0;i<MAX;i++){
          while(x[i][0]==0){
              x[i][0]=rand1();
          }
          while(x[i][1]==0||x[i][1]==x[i][0]){
              x[i][1]=rand1();
          }
          x[i][2]=(x[i][0] ^ x[i][1]);
      }
      vector<int>rw(n+1,0);
      vector<int>v1(MAX,0);
      for(int i=0;i<n;i++){
          rw[i+1]=rw[i];
          for(int j=0;j<v[a[i]].size();j++){
              rw[i+1]^=x[v[a[i]][j]][v1[v[a[i]][j]]%3];
              v1[v[a[i]][j]]++;
          }
      }
      for(int i=0;i<q;i++){
          if(rw[l[i]-1]==rw[r[i]]){
              cout<<"Yes"<<endl;
          }else{
              cout<<"No"<<endl;
          }
      }

 

ABC 237

E - Skiing

N个城市,每个城市有自己的高度,m次查询,如果 X高于Y,则X前往Y的幸福度是HX-HY

如果 X低于Y ,X前往Y的幸福度 是 -2*(HY-HX),问从1开始前往其他城市能得到的最大幸福度是多少

考虑以下几种情况

H1>H2>H3 此时幸福度为(H1-H2)+(H2-H3)=H1-H3

H1<H2<H3 此时幸福度为 -2(H2-H1)-2(H3-H2)=-2(H3-H1)=H1+H1-H3-H3

H1>H2<H3 此时幸福度为(H1-H2)-2(H3-H2)=H1+H2-H3-H3

H1< H2 >H3 此时幸福度为 -2(H2-H1)+(H2-H3)=H1+H1-H2-H3

由此我们可以得到,加入HX>=HY,我们就建边时 边权为0,如果HX<HY,我们就建边为HY-HX

那么1到每一个点的长度就是H1-Hi -di

for(int i=1;i<=m;i++){
        int x,y;
        cin>>x>>y;
        if(b[x]>=b[y]){
            a[x].push_back(make_pair(y,0));
            a[y].push_back(make_pair(x,b[x]-b[y]));
        }else{
            a[x].push_back(make_pair(y,b[y]-b[x]));
            a[y].push_back(make_pair(x,0));
        }
    }
for(int i=1;i<=n;i++){
        long long haha=-d[i];
        ans=max(ans,b[1]-b[i]+haha);
    }
    cout<<ans<<endl;

 

F - |LIS| = 3

总长度为n,可以使用的数字是1-m,构造最大上升序列的长度一定是3的数列有多少种方案

我们序列是(1,4,2,5,3) 我们应该在(1,2,3)的基础上思考其添加的数,显然是不能超过3,因此存在一个取min的过程

dp[n][长度为1序列结尾的结尾最小数][长度为2序列结尾的最小数][长度为3序列结尾的最小数]

因此我们思考这个问题

比如说 2,4,4,2 这是一个dp(5)(2)(4)(空)的情况

但是我那么如果加一个1 则就变成了dp(6)(1)(2)(4),因此上面的方案数完全可以归到此方案中

我们把m+1定义为空

上升长度是3,因此我们只需要枚举上升序列的结尾三个数即可进行转移

for(int x=1;x<=min(j,m);x++){
    dp[i][x][k][u]=(dp[i][x][k][u] + dp[i-1][j][k][u])%mod;
}
for(int x=j+1;x<=min(k,m);x++){
    dp[i][j][x][u]=(dp[i][j][x][u] + dp[i-1][j][k][u])%mod;
}
for(int x=k+1;x<=min(u,m);x++){
    dp[i][j][k][x]=(dp[i][j][k][x] + dp[i-1][j][k][u])%mod;
}

 

ABC 236

E - Average and Median(板子题)

一共有n个数,对于每个i,第i个和第i+1个必须选择一个数,问选出的最大平均数以及选出的最大中位数是多少?

思路:平均数而言,我们可以进行实数二分来进行得出。我们令mid为平均数,那么让a[i]-mid 即可得出ai对平均数的贡献,再运用一个线性dp来求出1-n扫一遍后的最大值,直到左右端点差值达到精度为止

while(r-l>1e-4){
        double mid=(l+r)/2;
        vector<double>dp(n+1);
        for(int i=1;i<=n;i++){
            dp[i]=-1e18;
            if(i<3){
                dp[i]=0;
            }
            if(i>1){
                dp[i]=max(dp[i-1],dp[i]);
            }
            if(i>2){
                dp[i]=max(dp[i],dp[i-2]);
            }
            dp[i]+=a[i]-mid;
        }
        if(max(dp[n],dp[n-1])>=0){
            l=mid;
        }else{
            r=mid;
        }
    }

 

再来考虑中位数,如果x作为这个长度为y序列的中位数,那么一定有1-(y/2)个数小于等于x,且(y/2)+1-y是大于等于x的,因此我们类比平均数一样来推出方程。比如 ai>=mid ,dpi++,ai<mid,dpi--。如果最后总和大于0,那么就可以得到中位数为mid的情况

    int L=0,R=1e9;
    while(L!=R){
        int mid=(L+R+1)/2;
        vector<int>dp(n+2);
        for(int i=1;i<=n;i++){
            dp[i]=-1e9;
            if(i<3){
                dp[i]=0;
            }
            if(i>1){
                dp[i]=max(dp[i],dp[i-1]);
            }
            if(i>2){
                dp[i]=max(dp[i],dp[i-2]);
            }
            if(a[i]>=mid){
                dp[i]++;
            }else{
                dp[i]--;
            }
        }
        if(max(dp[n],dp[n-1])>0){
            L=mid;
        }else{
            R=mid-1;
        }
    }

 

F - Spices(板子题)

1-2^n-1选几个数,使得其编号异或下来 能得到1到2的n次方减一 中所有数 (这个目前存在一些异或,看完题解后不太理解为什么能转换成线性基模板问题)

    sort(a+1,a+cnt+1);
    for(int i=1;i<(1<<n);i++){
        for(int j=62;j>=0;j--){
            if(!(a[i].second>>j))continue;
            if(!p[j]){
                p[j]=a[i].second;
                ans+=a[i].first;
                break;
            }else{
                a[i].second^=p[j];
            }
        }
    }

 

 

posted @ 2022-02-15 19:00  AShensky  阅读(127)  评论(0)    收藏  举报