首页 > 其他分享 >Codeforces Round #766 (Div. 2)

Codeforces Round #766 (Div. 2)

时间:2022-11-04 16:03:05浏览次数:80  
标签:vis int ++ 766 Codeforces cin color pd Div

Codeforces Round #766 (Div. 2)

img

\(\color{Green}{★}\) 表示赛时做出。
\(\color{Yellow}{★}\) 表示赛后已补。
\(\color{Red}{★}\) 表示 \(\text{To\ be\ solved}\)。

VP

A B C D
7 min 16 min 32 min 49 min
+ + + +

img

A. Not Shading

\(\color{Gray}{800}\) \(\color{Green}{★}\)

Description

给定一个 01 矩阵,每次能将有 1 的一列或一行全染成 1。

求问让给定位置变为 1 的最小操作次数。

Solution

分类讨论。

  • 所求位置本来就是 1,输出 0.
  • 所求位置所在行或列有 1,输出 1.
  • 矩阵中有 1,输出 2.
  • 输出 -1。

思路不难出,码量对于 A 略大。

code
void work(){
  int n,m,x,y;cin>>n>>m>>x>>y;
  vector<string>a(n);
  for(auto &i:a) cin>>i;
  if(a[x-1][y-1]=='B') return cout<<"0\n",void();
  bool f1=0,f2=0;
  L(i,0,m-1) f1|=(a[x-1][i]=='B');
  L(i,0,n-1) f1|=(a[i][y-1]=='B');
  if(f1) return cout<<"1\n",void();
  L(i,0,n-1) L(j,0,m-1) f2|=(a[i][j]=='B');
  cout<<(f2?2:-1)<<'\n';
}

B. Not Sitting

\(\color{Green}{1300}\) \(\color{Green}{★}\)

Description

有大小为 \(n \times m\) 的网格图,A 可以先将 \(k\) 个位置涂上色,\(B,A\) 先后选择一个位置,要求 \(B\) 选择的位置未上色。

\(B\) 希望尽可能接近 \(A\),\(A\) 希望尽可能远离 \(B\),对于 \(k \in [0,n \cdot m -1]\),求 \(A,B\) 之间的曼哈顿距离。

Solution

  • \(B\) 要接近 \(A\),显然最优策略就是坐中心;
  • \(A\) 要远离 \(B\),显然最优策略就是坐四角。

问题转化为每个点到四角的距离最大值。
全部压入一个数组,排序后输出即可。

code
void work(){
  cin>>n>>m;a.clear();L(i,1,n) L(j,1,m) 
    a.pb(max({i+j-2,i-1+m-j,n-i+j-1,n-i+m-j}));
  a.resize(n*m);sort(all(a));
  for(int i:a) cout<<i<<' ';cout<<'\n';
}

C. Not Assigning

\(\color{Cyan}{1400}\) \(\color{Green}{★}\)

Description

给定一棵 \(n\) 个节点的树,要求构造树上所有边和相邻边对的权值和都是质数或输出无解。

Solution

首先,如果两个质数的和仍是质数,则其中之一必然是 \(2\)。

所以,如果一个节点的度数超过 \(2\),必然无解,因为肯定会出现奇数相加的情况。

否则,dfs 遍历整棵树,对于每条边,分别用类似黑白染色的方式,赋值为 \(2,x\)(\(x\) 是质数,且 \(x+2\) 也是质数)

一开始看错题,以为质数不能重复,白亏了两个 dfs。

code
void dfs(int u,int fa,int x){
  for(pi v:g[u]) if(F(v)^fa)
      ans[S(v)]=x,dfs(F(v),u,x^1);
}void work(){
  cin>>n;L(i,1,n) g[i].clear();
  int rt,x,y,m=0;
  L(i,2,n) cin>>u>>v,g[u].pb({v,i}),g[v].pb({u,i});
  L(i,1,n){
    if(si(g[i])==1) rt=i;
    if(si(g[i])>2) return cout<<"-1\n",void();
  }dfs(rt,0,2);L(i,2,n) cout<<ans[i]<<" \n"[i==n];
}

D. Not Adding

\(\color{Purple}{1900}\) \(\color{Green}{★}\)

Description

给定一个长度为 \(n\) 的序列 \(\left\{ a_1,a_2,\dots,a_n \right\}\)。

每次操作可以选择两个数 \(a_i,a_j\),满足 \(\gcd(a_i,a_j)\) 不在序列中,将 \(\gcd(a_i,a_j)\) 加入序列。

求问最多的操作次数。

\(n \le 10^6,a_i \le 10^6\)

Solution

注意到 \(a_i\) 的值域范围很小,而且加入的数不会比原先的数大,所以完全可以枚举每个数是否在 \(a\) 中出现。

因为有 \(\gcd(\gcd(a_i,a_j),a_k) = \gcd(a_i,a_j,a_k)\),所以该问题的本质是求:从原序列中选出一个子序列,能够产生的 \(\gcd\) 有多少种。

对于每个值,直接枚举所有可能的倍数,并检查这些倍数的 \(\gcd\) 是否为当前数即可。

code
bool vis[N];int ans,f[N];
signed main(){
  FST;int n;cin>>n;int a;
  L(i,1,n) cin>>a,vis[a]=1,f[a]=a;
  L(i,1,1e6){
    ll(j,i,1e6,i) f[i]=__gcd(f[i],f[j]);
    if(f[i]==i&&!vis[i]) ans++;
  }cout<<ans;
}

E. Not Escaping

\(\color{Gold}{2200}\) \(\color{Yellow}{★}\)

Description

有一个 \(n \times m\) 的网络,要求从 \((1,1)\) 移动到 \((n,m)\)。

无法直接在竖直方向移动,在第 \(i\) 行横向移动每个单位扣除 \(x_i\) 的生命。

另有 \(k\) 个梯子,两端分别在 \((a,b),(c,d)\),攀爬可以恢复 \(h\) 的生命,不能回溯。

求问最小扣除的生命,或报告无解。

\(n,m,k \le 10^5\)

Solution

本题有一个很显然的思路,就是直接建图跑最短路,并且忽略掉那些平凡的节点。

因为有负权边,所以得用 SPFA,但是朴素的 SPFA 过不去,要加一些奇技淫巧去优化。详见 CF 讨论区和 Luogu 题解区。

考虑 DP。

还是像最短路那样,忽略掉除了起点、终点和所有梯子的端点之外的平凡点,这样就只剩下最多 \(2\cdot k+2\) 个点。

设 \(f_i\) 表示到达编号为 \(i\) 的节点时的最大生命。

因为题目要求不走回头路,所以可以按楼层递增,逐层进行 DP。

然后考虑 DP 的转移: (赛时就蚌在这)

对于某个点,显然有两种到达的方式,一种是同一层内的其他节点,另一种是通向这个节点的梯子

  • 通过同层其他节点转移

此时可以采用填表法进行 DP,转移方程为 \(f_i =\max\left( f_i,f_j-dis_{i,j}\times x_{c} \right)\)。

因为有左右两个转移方向,所以要正反扫两遍进行转移。

  • 通过梯子转移

此时可以采用 刷表法进行 DP,转移方程为 \(f_{v_i} = \max\left( f_{v_i},f_i + h \right)\)。

因为题目要求最小损失,所以最后输出 \(-f_{ed}\) 即可。

code
int n,m,k,pd,a,b,c,d,h;
vector<pi>g[N];pi nt[N];
int x[N],f[N];
void work(){
  rd(n,m,k);pd=0;
  L(i,1,n) rd(x[i]),g[i].clear();
  g[1].pb({1,++pd});
  L(i,1,k){
    rd(a,b,c,d,h);
    g[a].pb({b,++pd});nt[pd]={pd+1,h};
    g[c].pb({d,++pd});nt[pd]={0,0};
  }g[n].pb({m,++pd});
  L(i,1,pd) f[i]=-INF;f[1]=0;
  L(i,1,n){
    sort(all(g[i]));
    L(j,1,si(g[i])-1) f[S(g[i][j])]=max(f[S(g[i][j])],
      f[S(g[i][j-1])]-(F(g[i][j])-F(g[i][j-1]))*x[i]);
    R(j,si(g[i])-2,0) f[S(g[i][j])]=max(f[S(g[i][j])],
      f[S(g[i][j+1])]-(F(g[i][j+1])-F(g[i][j]))*x[i]);
    for(pi x:g[i]) if(f[S(x)]!=-INF&&F(nt[S(x)])!=0)
      f[F(nt[S(x)])]=max(f[F(nt[S(x)])],f[S(x)]+S(nt[S(x)]));
  }if(f[pd]==-INF) wr("NO ESCAPE\n");else wr(-f[pd],'\n');
}

F. Not Splitting

\(\color{Red}{2700}\) \(\color{Yellow}{★}\)

Description

定义一个元素是由方格图中相邻方格组成的二元组。

称一个由若干元素组成的数组是强的,当且仅当存在一种方案,可以沿方格纸上的格线将方格纸分为两个全等的部分,使得数组中的每一个元素中包含的两个方格都处于同一部分。

给定一个 \(k\times k\) 的方格纸和 \(n\) 个元素,请求出至少需要删除多少个元素,才能使得剩余的元素组成的数组是强的。

\(n \le 10^5,k\le 500,k|2\)

Solution

Claim

所有满足条件的切割线都满足关于 \([\dfrac{k}{2},\dfrac{k}{2}]\) 即方格图中心点中心对称

Proof

考虑从中心点出发,随便找一条到达边缘的路线。

此时可以发现,要想构造出另外半条切割线并且满足题目中所述的条件,就一定要关于中心点进行中心对称来找。

严谨的证明请移步官方题解

Q.E.D


由此,我们可以转化题意,转变为求解 被切割的元素的最小值

考虑一手图论,把网格图里的交点看成点,每条边分割的元素个数看成边的权值,从中点出发跑最短路,只要到达边界处,就说明此时产生了最优的解。

因为最边缘一圈的边是不可能有贡献的,所以对边的编号和对点的编号并不完全相同,要加一点转化,具体看代码。

实现上,因为每条边有五个参数(其实可以只有四个),所以直接用 std::map<std::tuple<int,int,int,int>,int> 无脑解决即可。

关于 std::tuple,可以当成拓展的 std::pair,需要 C++11 以上的版本。

因为路线是关于中心点中心对称的,所以在更新 \((i,j)\) 的答案同时还要把 \((k-i,k-j)\) 一起带上。

code
int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
int n,k,a,b,c,d;
map<tuple<int,int,int,int>,int>mp;/*same as edge value*/
bool vis[510][510];
struct nd{int w,x,y;friend bool operator<(nd a,nd b){return a.w>b.w;}};
void work(){
  cin>>n>>k;me(vis,0);mp.clear();
  priority_queue<nd>q;
  L(i,1,n){
    cin>>a>>b>>c>>d;if(a>c) swap(a,c);if(b>d) swap(b,d);
    if(a^c) mp[{a,b-1,a,b}]++,mp[{a,b,a,b-1}]++;
    else mp[{a-1,b,a,b}]++,mp[{a,b,a-1,b}]++;
  }
  //only edges in the middle can make value,
  //because if got border,then return.
  //the id should change.
  q.push({0,k/2,k/2});
  while(!q.empty()){
    auto u=q.top();q.pop();if(vis[u.x][u.y]) continue;
    vis[u.x][u.y]=vis[k-u.x][k-u.y]=1;
    if(!u.x||!u.y||u.x==k||u.y==k) 
      return cout<<n-u.w<<'\n',void();
    L(i,0,3){
      nd v={0,u.x+dx[i],u.y+dy[i]};
      v.w=u.w+mp[{u.x,u.y,v.x,v.y}]+
        mp[{k-u.x,k-u.y,k-v.x,k-v.y}];
      q.push(v);
    }
  }
  //and why this can work as an dijstra?
  //regard it as two points run on graph.
}

标签:vis,int,++,766,Codeforces,cin,color,pd,Div
From: https://www.cnblogs.com/AIskeleton/p/16858124.html

相关文章

  • Codeforces Round #820 (Div. 3) F
    F.KireiandtheLinearFunction首先我们知道的是给定长度w都是%9意义下的所以我们枚举四个位置的数就是9^4预处理出来答案这里我们要去w长度的串%9但是w的位数过......
  • Codeforces Global Round 20 D F1 F2/more
    https://codeforc.es/contest/1672F1https://www.luogu.com.cn/blog/AlexWei/solution-cf1672f1将置换分解为若干轮换(环),悲伤值越大\(\Rightarrow\)环越少(设环为\(k......
  • Contest 2050 and Codeforces Round #718 (Div. 1 + Div. 2) D
    D.ExplorerSpace我们考虑一个性质就是他最多就是找到一条k/2的最短路径然后回来这样是肯定是包含最优解的这个观察第二个样例我们将其改变一下要是我们一半的长度......
  • CodeForces 1540B Tree Array
    CF传送门洛谷传送门很强的一个题。发现根的选择很重要,于是考虑先枚举根。考虑枚举两个点对\(i,j\(i<j)\),如果\(j\)比\(i\)先被标记,那么\(i,j\)就贡献了一个逆......
  • CodeForces - 204A Little Elephant and Interval
    题意:给定区间[l,r],问区间内有多少第一个数字和末尾数字一样的数。解:练习一些数位dp。先从高到低dp[pos][fir]表示dp到第pos个位置,以fir开头的串的数量,其中fir不为0。这道......
  • Manthan, Codefest 18 (rated, Div. 1 + Div. 2) (E,F,G)
    LinkEm次操作,每次加边后询问最大旅行团。旅行团的定义:旅行团中的每个人都至少有k个邻接点在团里。显然会肯定很想全选,但是有的人压根没有k个邻接点,只能直接删掉,然后一......
  • Codeforces Round #610 (Div. 2) C
    C.PetyaandExamhttps://codeforces.com/contest/1282/problem/C考虑贪心先对于时间排序然后贪心我们可以不考那我们可以在此之前离开然后在离开之前这段时间多做......
  • np.divide()
    用例:numpy.divide(x1,x2,/,out=None,*,where=True,casting=‘same_kind’,order=‘K’,dtype=None,subok=True[,signature,extobj])=<ufunc‘true_divide’......
  • Codeforces Round #634 (Div. 3) E
    E2.ThreeBlocksPalindrome(hardversion)我们考虑一种最优构造对于一个数x我们肯定要的是他的前几个再最后几个中间选最多的一个数这样显然是最优的我们枚举x......
  • Codeforces Global Round 7 D
    D1.Prefix-SuffixPalindrome(Easyversion)easy版本我们只需要n2dp预处理出快速判断回文串然后我们再通过双指针O(n)求解最大串intdp[5010][5010];voidsolve(){......