首页 > 其他分享 >AtCoder Beginner Contest 285(B,D,E,F)

AtCoder Beginner Contest 285(B,D,E,F)

时间:2023-05-06 20:58:34浏览次数:45  
标签:AtCoder const Beginner int long maxn include 285 define

AtCoder Beginner Contest 285(B,D,E,F)

B (暴力,非二分)

B

这道题其实很简单,但是我在\(vp\)的过程,有了一个错误的认识,纠正一下

那就是,我把这个当成了一个二分题,并且还有点坚定不移,后来细想,发现不对

二分,适用于那种边界分明的那种题(左边一定是符合条件,右边一定符合条件,也可表达为线性问题那种)这种的,但是这个题不符合这个条件,不符合(万一右边出现了一个符合条件的,但是左边也可能存在不符合条件的)

所以这道题直接暴力即可

#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 LL long long
#define ios  ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define inf 1e18
#define INF 1e18
#define mem(a,b) memset((a),(b),sizeof(a))
const double eps=1e-9;
const int maxn=1e6+10;
const int mod=998244353;
int n;
string s;
int solve(int pos)
{
   int ans=n-pos;
   for (int i=1;i<=n-pos;i++)
   {
      if(s[i]==s[i+pos])
      {
         ans=i-1;
         break;
      }
   }
   return ans;
}
signed main() 
{
   cin>>n>>s;
   s=" "+s;
   for (int i=1;i<n;i++)
   {
     cout<<solve(i)<<"\n";
   }
   system ("pause");
    return 0;
}

D (图,拓扑排序)

D

为什么每次我都没有看懂题意,好难过

这个题的大意就是每个人都会使用一个字符串\(s\),但是每个人都想要换这个字符串\(s\)换成\(t\),但是这个换字符串还有一个条件很关键,那就是那个\(t\)是没有正在被其他人所使用的

题目问是否可以让每个人都可以换成功(我一开始以为是只要一个人换成功就好了,我就在想,怎么会有这么简单的\(D\)题)

正难则反,我们不太可能找到一个正确的换字符串的顺序,我们可以找有哪些是一定换不了的

然后,我们发现这个换来换去的有点像图,如果把这个过程当成图来看(有向边),如果有一个环的话,那么这个环里面的字符都是换不成功的了,所以我们只需要找到环即可(找到,就是\(No\))

对于找环,有两种方法

一种是直接\(dfs\),如果出现了一个点到了\(2\)次以上,那么一定存在一个环

一种是拓扑排序,判断进入队列里面是否有\(n\)个

题目里面的字符串我对他们进行了离散化

我这里用的是拓扑排序

#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 LL long long
#define ios  ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define inf 1e18
#define INF 1e18
#define mem(a,b) memset((a),(b),sizeof(a))
const double eps=1e-9;
const int maxn=1e6+10;
const int mod=998244353;
int n;
string s[maxn],t[maxn];
set<string>rk;
map<string,int>has;
int in[maxn];
vector<int>g[maxn];
signed main() 
{
   cin>>n;
   for (int i=1;i<=n;i++)
   {
      cin>>s[i]>>t[i];
      rk.insert(s[i]);
      rk.insert(t[i]);
   }
   int now=0;
   for (auto x:rk)
   {
      now++;
      has[x]=now;
   }
   for (int i=1;i<=n;i++)
   {
      int u=has[s[i]],v=has[t[i]];
      g[u].push_back(v);
      in[v]++;
   }
   queue<int>q;
   int cnt=now;
   for (int i=1;i<=now;i++)
   {
      if(in[i]==0)
      {
         q.push(i);
         cnt--;
      }
   }
   while (!q.empty())
   {
      int u=q.front();
      q.pop();
      for (auto x:g[u])
      {
         in[x]--;
         if(in[x]==0)
         {
            q.push(x);
            cnt--;
         }
      }
   }
   if(cnt)
   {
      cout<<"No\n";
   }
   else 
   {
      cout<<"Yes\n";
   }
   system ("pause");
    return 0;
}

E(dp,前缀和)

E

这个题目的大意就是有n天,我们可以选择任意天作为假期,我们每一天都会有一个效率

对于这一天是假期,那么这一天的效率为\(0\)

对于这一天是工作日,那么我们需要知道上一次假期离现在的天数\(x\),还需要知道现在距离下一次假期的天数,那么这一天的效率为\(A_{min(x,y)}\)

我们发现假期具体是哪一天不重要,重要的是这一天距离自己较近的那一天的距离是多少

所以干脆把第一天作为作为上一次工作的假期,这样就不用了担心后面寻找前一个不太好找了

然后我们可以设\(dp[i]\)为前\(i\)天,并且第\(i\)天是假期的最大效率

可以得到状态转移方程为

dp[i]=max(dp[i],dp[j]+(a[min(i-(j+1),(j+1)-j)]+a[min(i-(j+2),(j+2)-j]+....+a[min(i-(i-1),(i-1)-j)]+dp[j]))

我们发现那一段\(a\)的和好像是有规律的,对于那些里\(i\)近的,选择\(i\),可以是从\(1\)到中间,对于那些里\(j\)近的,选择\(j\),可以是从\(1\)到中间,但是注意最中间的那一个,离\(i\)和\(j\)一样,只需要加一次即可,我们可以直接通过前缀和预处理出来

故,对于\(l,r\)范围,可以直接得到\(sum[len/2]+sum[(len+1)/2]\)

然后我们可以得到一个更简单的状态转移方程

dp[i]=max(dp[i],dp[j]+get(j,i))

具体看代码

#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 LL long long
#define ios  ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define inf 1e18
#define INF 1e18
#define mem(a,b) memset((a),(b),sizeof(a))
const double eps=1e-9;
const int maxn=1e6+10;
const int mod=998244353;
int n;
int a[maxn],sum[maxn];
int dp[maxn];
int get(int l,int r)
{
   int len=(r-l-1);
   int L=len/2,R=(len+1)/2;
   return sum[L]+sum[R];
}
signed main() 
{
   cin>>n;
   for (int i=1;i<=n;i++)
   {
      cin>>a[i];
      sum[i]=sum[i-1]+a[i];
   }
   dp[1]=0;
   for (int i=2;i<=n+1;i++)
   {
      for (int j=1;j<i;j++)
      {
         dp[i]=max(dp[i],dp[j]+get(j,i));
      }
   }
   cout<<dp[n+1]<<"\n";
   system ("pause");
    return 0;
}

F(树状数组)

F

这个题大意很简单,就是给你一个字符串,有两种操作

一种是需要把\(x\)位置的字符改成\(ch\)

还有一种是需要判断对于此时的字符串\(s\),把里面的字符按从小到大的顺序排列可以得到一个字符串\(t\),我们需要判断的就是此时的\(s\)的\(l\)到\(r\)这一段字符串是不是\(t\)的子串

由题可知,要想为子串,\(s[l...r]\)必须是从小到大的,而且除了最前面的(最小的)和最后面的(最大的)(对于子串,我们都很熟悉,就是一个串删除前面若干个,删除后面若干个),这里面的字符数量都必须和整个字符串里面的字符数量一样,而且还必须连续

我们知道字符的数量不是很大,而且每修改一次,区间里面的字符数量都会改变

所以我们这里给每一个字符都创建了一棵树,用来记录这个字符的分布情况

前面都是很典型的树状数组

但是还有一点需要注意,我们这里的连续的判断,就是每次更新现在的位置,通过现在的位置和该字符的数量,判断这一小段里面是不是都是这一个字符,注意更新最新位置

具体操作可看代码

#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 LL long long
#define ios  ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define inf 1e18
#define INF 1e18
#define mem(a,b) memset((a),(b),sizeof(a))
const double eps=1e-9;
const int maxn=1e5+10;
const int mod=998244353;
int n,q;
string s;
int lowbit(int x)
{
   return x&(-x);
}
struct tree
{
   int bit[maxn];
   void add(int x,int val)
   {
      while (x<=n)
      {
         bit[x]+=val;
         x+=lowbit(x);
      }
   }
   int query(int x)
   {
      int res=0;
      while (x)
      {
         res+=bit[x];
         x-=lowbit(x);
      }
      return res;
   }
   int Query(int l,int r)
   {
      return query(r)-query(l-1);
   }
   
}tr[27];
signed main() 
{
   cin>>n;
   cin>>s;
   s=" "+s;
   for (int i=1;i<=n;i++)
   {
      int now=s[i]-'a';
      tr[now].add(i,1);
   }
   cin>>q;
   while (q--)
   {
      int op;
      cin>>op;
      if(op==1)
      {
         int pos;
         char ch;
         cin>>pos>>ch;
         int last=s[pos]-'a';
         tr[last].add(pos,-1);
         s[pos]=ch;
         int now=s[pos]-'a';
         tr[now].add(pos,1);
      }
      else 
      {
         int l,r;
         cin>>l>>r;
         int mx=0,mi=26;
         for (int i=0;i<26;i++)//还有这个寻找最大最小值,最好不要从字符串的l,r里面寻找,太慢了,我们只要知道这里面有没有这个字符即可
         {
            if(tr[i].Query(l,r)>0)
            {
               mx=max(mx,i);
               mi=min(mi,i);
            }
         }
         int pos=l;
         bool yes=true;
         for (int now=mi;now<=mx;now++)
         {
            int cnt=tr[now].Query(l,r);
            if(now!=mi&&now!=mx)
            {
               int all=tr[now].Query(1,n);
               if(cnt!=all)
               {
                  yes=false;
                  break;
               }
            }
            int ll=pos,rr=pos+cnt-1;
            int tot=tr[now].Query(ll,rr);
            if(tot!=cnt)
            {
               yes=false;
               break;
            }
            pos=rr+1;//注意边界,是rr而不是ll
         }
         if(yes)
         {
            cout<<"Yes\n";
         }
         else 
         {
            cout<<"No\n";
         }
      }
   }
   system ("pause");
    return 0;
}

标签:AtCoder,const,Beginner,int,long,maxn,include,285,define
From: https://www.cnblogs.com/righting/p/17378427.html

相关文章

  • [AtCoder-AT_ABC108_B]题解(C++)
    PartIPreface原题目(Luogu)原题目(AtCoder)PartIISketchPartIIIAnalysis观察这道题,我们很容易想到,必须推导出\(x1,y1,x2,y2\)与\(x3,y3,x4,y4\)之间的关系。我们观察下图。可以发现:\(\begin{aligned}\begin{cases}x3=x2-(y2-y1)\\y3=y2+(x2-......
  • AtCoder Regular Contest 131 E Christmas Wreath
    洛谷传送门AtCoder传送门不难猜想有解充要条件为\(n\ge5\)且\(\frac{n(n-1)}{2}\bmod3=0\)。发现如果钦定一个点的出边都为同一种颜色,那么条件\(2\)一定满足。那么题目等价于把\(\{0,1,...,n-1\}\)分成\(3\)组使得每组的和相等。这个东西可以随便dp做,也不......
  • AtCoder Regular Contest 131 D AtArcher
    洛谷传送门AtCoder传送门观察可以发现:使每支箭的距离都为\(D\)一定不劣;每支箭坐标一定为整数;设最左边的箭坐标为\(x\),那么\(x\)太小时可以把最左边的箭移到最右边,\(x\)太大时可以把最右边的箭移到最左边。计算可得\(x\)的最优取值范围为\(x\in[-\left\lfloor\fr......
  • AtCoder Regular Contest 128 E K Different Values
    洛谷传送门AtCoder传送门考虑判断有无解。把序列分成\(c=\left\lceil\frac{len}{k}\right\rceil\)段,则\(\foralla_i\lec\)且\(\sum\limits_{i=1}^n[a_i=c]\le((len-1)\bmodk)+1\)。必要性显然。充分性可以考虑直接构造,不难证明。考虑如何构造字典序最小......
  • AtCoder Regular Contest 134 D Concatenate Subsequences
    洛谷传送门AtCoder传送门我一年前甚至不会做/qd发现\(a_{x_1}\)为\(k=\min\limits_{i=1}^na_i\)时最优。然后开始分类讨论:如果\(\min\limits_{a_i=k}a_{i+n}\lek\),答案为\((k,\min\limits_{a_i=k}a_{i+n})\)。这是因为如果再选一个\(k\)肯定不优。否则......
  • AtCoder Beginner Contest 300
    A-N-choicequestion#include<bits/stdc++.h>usingnamespacestd;intread(){intx=0,f=1,ch=getchar();while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();if(ch=='-......
  • AtCoder Regular Contest 128 D Neq Neq
    洛谷传送门AtCoder传送门考虑把所有\(a_i=a_{i+1}\)的位置断开,分别计算然后把方案数乘起来。接下来的讨论假设\(a_i\nea_{i+1}\)。考虑一个dp,设\(f_i\)为\([1,i]\)最后剩下的集合的方案数。转移显然是\(f_i\getsf_i+f_j\),但是需要满足\((a_j,a_{j+1},...,......
  • AtCoder Beginner Contest 242(D,E)
    AtCoderBeginnerContest242(D,E)D(二叉树搜索)D题目大意就是首先给你一个字符串,代表\(S^0\),然后我们可以操作得到\(S^1,S^2\)等等我们可以知道\(S^i\)是拿\(S^(i-1)\)经过一系列替换而来的,因为这个字符串只有三种字符串,\(A,B,C\),这个替换方式就是把\(A\)替换成\(BC\),把\(B\)......
  • AtCoder Regular Contest 125 F Tree Degree Subset Sum
    洛谷传送门AtCoder传送门首先将度数\(-1\)。设\(f_i\)为体积为\(i\)至多能用几个物品凑出来,\(g_i\)为至少。我们现在要证明一个东西:\(x\in[g_i,f_i]\),\((i,x)\)合法。首先若\((s,x)\)合法,那么必须满足\(s-x\in[-z,z-2]\),其中\(z=\sum\limits_{i=1}......
  • AT_abc106_d [ABC106D] AtCoder Express 2 题解
    题目传送门解题思路区间\(dp\)。划分阶段:以左右城市之间的列车数量为阶段。状态表达:设\(f_{i,j}\)为城市\(i\)与城市\(j\)之间的列车数量。状态转移:由图可知,城市\(l\)与城市\(r\)之间的列车数量,就是城市\(l\)与城市\(r-1\)之间的列车数量与城市\(l+1\)与......