首页 > 其他分享 >AtCoder Beginner Contest 253(E,F)

AtCoder Beginner Contest 253(E,F)

时间:2023-05-17 16:55:55浏览次数:40  
标签:AtCoder Beginner int define ans 253 include id op

AtCoder Beginner Contest 253(E,F)

E (dp,前缀和)

E

大意就是求满足以下要求的的序列的个数

\(1\),满足\(a_i\)都在\([1,m]\)的范围里面

\(2\),满足$ \vert a_i-a_{i+1}\vert $ 大于\(k\)

之前做过一个类似的题目,是绝对值小于\(k\),不过大同小异

这里我使用了前缀和来优化

但是这里\(k\)是可以为\(0\)的,我没有特判,错了,所以我们最好特判为\(0\)的情况

#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=1000+10;
const int maxm=5000+10;
const int mod=998244353;
int n,m,k;
int dp[maxn][maxm];
int sum[maxn][maxm];
int add(int x,int c)
{
   x+=c;
   if(x>=mod)
   {
      x-=mod;
   }
   else if(x<0)
   {
      x+=mod;
   }
   return x;
}
signed main() 
{
  ios;
   cin>>n>>m>>k;
   if(k==0)
   {
    int ans=1;
    for (int i=1;i<=n;i++)
    {
        ans=(ans*m)%mod;
    }
    cout<<ans<<"\n";
    system ("pause");
    return 0;
   }
   for (int i=1;i<=m;i++)
   {
      dp[1][i]=1;
      sum[1][i]=add(sum[1][i-1],dp[1][i]);
   }
   for (int i=2;i<=n;i++)
   {
      for (int now=1;now<=m;now++)
      {
         if(now>k)
         {
            int l=1,r=now-k;
            int tmp=add(sum[i-1][r],-sum[i-1][l-1]);
            dp[i][now]=add(dp[i][now],tmp);
         }
         if(now+k<=m)
         {
            int l=now+k,r=m;
            int tmp=add(sum[i-1][r],-sum[i-1][l-1]);
            dp[i][now]=add(dp[i][now],tmp);
         }
         sum[i][now]=add(sum[i][now-1],dp[i][now]);
      }
   }
   int ans=sum[n][m];
   cout<<ans<<"\n";
   system ("pause");
   return 0;
}

F(树状数组)

F

这个题大意就是有一个矩阵,初始每一个位置上都是\(0\),有下面几种操作

操作\(1\),输出\(l,r,x\),把\(l\)到\(r\)列都加上\(x\)

操作\(2\),输出\(pos,x\),把\(pos\)行所有的数都变成\(x\)

操作\(3\),输出\(r,x\),输出此时这个位置上的数

我这里把每一个操作的顺序看做了“时间”。

我们可以发现,能真正影响最后的答案的是第二个操作,所以,每次查询答案,我们首先要知道这个位置最后一次(到查询为止)被替换的值是多少,然后我们再来加上被替换之后进行了操作\(1\)改变了多少

查询这一次查询行位置的最后一次替换值很简单,我们可以在进行操作\(2\)的时候对\(last[pos]\)记录下来

然后我们还需要知道在最后一次替换到目前的操作\(1\)进行了多少,可是我们发现如果按照时间的顺序来,不仅要判断这个是否在操作\(1\)的范围,还有可能前面的操作\(1\)可能会有很多,导致超时,所以在操作\(3\)的时候再一个一个来变是行不通的

既然是以操作\(2\)的时间为界限的,那么不如我们先保存操作,然后每次在操作\(2\)的时候,来判断这一个操作\(2\)会对哪些答案会存在影响,然后一律把那些受到影响的值顺便变成这一个操作替换后的值,然后我们再考虑操作\(1\)的影响,但是我们一个一个找显然是不太现实的,既然是求求这一段时间到查询这一段时间里面变化的数,不如把时间看做下标,对一段里面的加上的值进行一次前缀和,我们可以先减去前面用不到的,操作\(1\)按照时间对前缀和数组不断更新,直到查询的时候,加上这个位置此时的前缀和,那么答案就是操作\(2\)的\(x\)加上这一段时间这一列的变化值\(val\)

对于以上的前缀数组,我们这里用的是树状数组,只不过下标是行的位置,按照时间的顺序来进行加减

#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=2e5+10;
const int mod=998244353;
int n,m;
int q;
int bit[maxn];
struct node
{
   int op,l,r,x;
   //op==1,  op,l,r,x
   //op===2,  op,pos,x,id
   //op==3     op,x,y,ans_id,第几个答案
}a[maxn];
vector<int>ans;
int last[maxn];
vector<int>Queryin[maxn];
int lowbit(int x)
{
   return x&(-x);
}
void update(int x,int val)
{
   while (x<=m)
   {
      bit[x]+=val;
      x+=lowbit(x);
   }
   return ;
}
void Update(int l,int r,int val)
{
   update(l,val);
   update(r+1,-val);
   return ;
}
int query(int x)
{
   int res=0;
   while (x)
   {
      res+=bit[x];
      x-=lowbit(x);
   }
   return res;
}
void op1(int id)
{
   int l,r,x;
   cin>>l>>r>>x;
   a[id]={1,l,r,x};
   return ;
}
void op2(int id)
{
   int pos,x;
   cin>>pos>>x;
   last[pos]=id;
   a[id]={2,pos,x,id};
   return ;
}
void op3(int id)
{
   int x,y;
   cin>>x>>y;
   int ans_id=ans.size();
   ans.push_back(0);
   int last_x=last[x];
   Queryin[last_x].push_back(id);//Queryin里面记录的是第last_x时操作二影响的查询操作的答案,在后面进行操作的时候对答案赋初值,并减去这时间前面的列的变化
   a[id]={3,x,y,ans_id};
   return ;
}
void solve1(int id)
{
   auto [op,l,r,x]=a[id];
   Update(l,r,x);
   return ;
}
void solve2(int i)
{
   auto [op,pos,x,id]=a[i];
   for (auto effect:Queryin[id])
   {
      auto [opp,posx,posy,ans_id]=a[effect];
      ans[ans_id]=x;//赋初值
      ans[ans_id]-=query(posy);//减去前面的变化
   }
   return ;
}
void solve3(int id)
{
   auto [op,x,y,ans_id]=a[id];
   ans[ans_id]+=query(y);//加上查询此时的列的总变化,实现了前缀和的效果
   return ;
}
signed main() 
{
   cin>>n>>m>>q;
   for (int i=1;i<=q;i++)
   {
      int op;
      cin>>op;
      if(op==1)
      {
         op1(i);
      }
      else if(op==2)
      {
         op2(i);
      }
      else if(op==3)
      {
         op3(i);
      }
   }
   for (int i=1;i<=q;i++)
   {
      int op=a[i].op;
      if(op==1)
      {
         solve1(i);
      }
      else if(op==2)
      {
         solve2(i);
      }
      else if(op==3)
      {
         solve3(i);
      }
   }
   for (auto x:ans)
   {
      cout<<x<<"\n";
   }
   system ("pause");
   return 0;
}

标签:AtCoder,Beginner,int,define,ans,253,include,id,op
From: https://www.cnblogs.com/righting/p/17409292.html

相关文章

  • AtCoder Beginner Contest 200 F Minflip Summation
    洛谷传送门AtCoder传送门显然的策略:选择全部\(0\)段变成\(1\),或选择全部\(1\)段变成\(0\)。归纳可得一般性的结论:设字符串中\(s_i\nes_{i+1}\)的位置数为\(k\),答案为\(\left\lceil\frac{k}{2}\right\rceil\)。因为在模意义下不能上取整,考虑记\(k\)的奇偶性(这样......
  • AtCoder Regular Contest 160 D Mahjong
    洛谷传送门AtCoder传送门搞笑题,我不会做,我更搞笑。考虑逆序操作,即初始有一个全\(0\)序列,每次单点加\(k\)或者长为\(k\)区间加\(1\)。考虑把一个操作集合唯一对应到一个最终序列,不难发现只要限制每个区间加\(1\)的次数\(<k\)即可。因为如果正序操作,加上了限制,每个......
  • AtCoder Beginner Contest 301 F Anti-DDoS
    洛谷传送门AtCoder传送门考虑分类计数,讨论“没有DD”、“有DD无o”、“有DDo无S”三种情况。没有DD,枚举有几种大写字母出现过;剩下两种情况,考虑设\(f_{i,0/1}\)分别表示两种情况的方案数。\(f_{i,0}\)可以从\(f_{i-1,0}\)填大写字母转移,也可以枚举第一个出现两......
  • Atcoder Regular Contest 101
    F-RobotsandExits\(n\)个人,\(m\)个出口在一条数轴上,坐标是正整数。你每次可以让所有人向左或向右一步,人在某个出口上后就离开。求多少种操作的方案使得人全部走光。两个方案相同当且仅当存在至少一个人在两次操作序列进行完成后从不同的出口消失。对\(10^9+7\)取模。\(1......
  • AtCoder Beginner Contest 223 G Vertex Deletion
    洛谷传送门AtCoder传送门设\(f_{u,0/1}\)为\(u\)的子树,\(u\)是否在匹配内的最大匹配数。注意到对于一个匹配,在它深度较浅的点上才会被计入答案。转移大概是\(f_{u,0}\)取\(\sum\limits_{v\inson_u}\max(f_{v,0},f_{v,1})\),\(f_{u,1}\)要从儿子中选一个\(f_{v,0......
  • AtCoder Beginner Contest 301
    title:AtCoderBeginnerContest301categories:算法题解description:咕咕咕tags:-Atcoder-贪心-BFS-DPcover:/img/chino/vec/chino17.jpgkatex:truedate:2023-05-1322:47:31A-OverallWinner(abc301a)题目大意给定一个字符串表示高桥和青木......
  • AtCoder Regular Contest 129 C Multiple of 7
    洛谷传送门AtCoder传送门首先\(\text{7777...777}\)(\(x\)个\(7\))对能被\(7\)整除子串数量的贡献是\(\frac{x(x+1)}{2}\)。把\(n\)分解成若干\(x_i\)使得\(\sum\limits_{i=1}^m\frac{x_i(x_i+1)}{2}=n\),表示每段\(x_i\)个\(7\)。怎么把它们组合在一起呢?一个......
  • AtCoder Beginner Contest 163 F path pass i
    洛谷传送门AtCoder传送门感觉我的做法比较奇葩(容斥,总路径数减去只走点权为\(k\)的路径。设点权为\(k\)的点数为\(c_k\),点权不为\(k\)的点构成的每个连通块大小为\(s_i\),那么\(ans_k=\frac{n(n-1)}{2}-\sum\frac{s_i(s_i-1)}{2}+c_k\)。考虑快速计算\(\su......
  • AtCoder Beginner Contest 207 F Tree Patrolling
    洛谷传送门AtCoder传送门简单树形dp。设\(f_{u,i,p=0/1,q=0/1}\)为\(u\)的子树中被覆盖点数为\(i\),\(u\)有没有被覆盖,\(u\)有没有被选。转移树形背包合并即可,需要分类讨论。要注意如果\(u\)没被覆盖,\(v\)选了,或者\(u\)选了,\(v\)没被覆盖,被覆盖点数要\(+1\)。......
  • AtCoder Beginner Contest 177 F I hate Shortest Path Problem
    洛谷传送门AtCoder传送门设\(f_{i,j}\)为从第\(1\)行到\((i+1,j)\)的最短路。因为我们并不关心最后到达的是哪一个格子,于是强制\(f_{i,j}\)为必须从\((i,j)\)往下走一格到\((i+1,j)\)的最短路。有转移:\[f_{i,r+1}\getsf_{i-1,j}+r+2-j,j\in[l......