还是常规的过了A,B,C还是在D上卡了...

D. The Strongest Build

简化题意:给定你n组东西,每组东西都有\(c_i\)个装备,每个装备有一个武力值\(a_{i,j}\),要求你从每一组中选出一个装备,使得总的装备的武力值最大。但有一些给定的方案是不能选的。

首先看到这个题的第一印象是只有\(m\)中方案是不能选的。那我们直接从最大的方案开始,一点点的调整为次大的,第三大的,第四大的,....,最多也就调整m次。但发现这个调整真的太难了...实现不了。其实想一下,这个题总的方案数也就\(\prod c_i\)这么多。我们可以直接对其进行搜索。但直接搜索全部的方案显然不太现实,我们依据一下原则:

1:初始的策略就是先选每一组中的最大值。

2:若当前这个策略不合格,则将当前策略中的某一组,将它的装备降为下一级,形成一个新的策略。

3:对所有当前的策略,优先取出和最大的策略进行检查。若不合法,则按照2生成其他的策略,否则,直接退出当前值为最大值。

总的贪心的思路和dijkstra算法差不多,不过算法形式和应用范围相差却深远。

学习某个算法的形式真的解决不了太多问题,掌握了其真正的思路与核心才掌握了它的一切!

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10,MN=15;
int n,m,c[MN],a[MN][N];
map<vector<int>,bool>mp;
map<vector<int>,bool>vis;
priority_queue<pair<int,vector<int> > >q;
int main()
{
// freopen("1.in","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&c[i]);
for(int j=1;j<=c[i];++j) scanf("%d",&a[i][j]);
}
vector<int>s;
scanf("%d",&m);
for(int i=1;i<=m;++i)
{
for(int j=1;j<=n;++j)
{
int x;scanf("%d",&x);
s.push_back(x);
}
mp[s]=1;s.clear();
}
int s1=0;
for(int i=1;i<=n;++i) s.push_back(c[i]),s1+=a[i][c[i]];
vector<int>ans;q.push({s1,s});vis[s]=1;
while(!q.empty())
{
pair<int,vector<int> >x=q.top();q.pop();
if(mp.find(x.second)==mp.end()) {ans=x.second;break;}
for(int i=1;i<=n;++i)
{
if(x.second[i-1]>=2)
{
x.second[i-1]--;
if(vis.find(x.second)==vis.end())
{
q.push({x.first-a[i][x.second[i-1]+1]+a[i][x.second[i-1]],x.second});
vis[x.second]=1;
}
x.second[i-1]++;
}
}
}
for(auto x:ans) printf("%d ",x);
return 0;
}

E. Coloring

这个E题真的好难搞啊!!!

要被虐哭了都,题解翻译过来根本看不懂,只能一边看官方题解代码,一边试图理解题解里的话。

只能慢慢的整理思路....

首先遇到这种题不要慌!!!先尝试着随便写写,列举下,找找规律,这种题肯定是找到某种性质,然后根据性质做题。

这个题就直接说了,首先根据随便写写,随便推推可以发现一个重要的性质(这只能靠内力了):

如果有两个同一行格子相邻且数字相同的话,那么他们这两个格子所在的两列就唯一确定了。

例如如果(3,3)和(3,4)都为1,则(2,3)和(2,4)只能为0,(4,3)和(4,4)只能为0,(1,3)和(1,4)只能为1,(5,3)和(5,4)只能为0,....可以一直推下去,这两列就唯一确定了(我们成为列条带)。如果是同一列中两个相邻数字相同,则这两行也唯一确定(我们成为横条带),和上述类似。

那么我们知道了两个相邻格子的会使两列唯一确定。接下来考虑什么情况下会出现两个相邻格子数字相同的情况,稍微推一下就能得知:1.当同一行有两个格子数字相同且中间的格子个数为偶数,那么这之间一定会有两个相邻的格子数字相同。比如(3,3)和(3,6)两个格子都是1,不管(3,4)和(3,5)怎么填,都会有两个相邻的格子数字相同的情况出现。2.当同一行有两个格子数字不同且中间的格子数为奇数的话,那么这之间一定会有两个相邻的格子数字相同。刺入(3,3)为1,(3,5)为0,不管你咋填,都一定会有两个相邻的格子数字相同。

同时我们会发现若同时有横条带和列条带的话,那么这两种条带一定会冲突,因为你会发现列条带相邻行是不同的,而横条带却要求相邻行要相同。最简单的例子就是一个\(2\times2\)的格子,(1,1)和(1,2)都为1,那么根据列条带的话,则(2,1)和(2,2)也应该为0,但如果使(2,1)也为1则出现横条带两者冲突。

所以合法的局面是只能出现横条带或列条带,或不出现条带。

分开考虑:若不出现条带,即不能出现格子相邻且数字相同,发现这个一共就两种。0101010...和1010110全部错开就行。

列条带:我们考虑若有列条带的话,(即如果同一行有两个数字相同的格子他们中间的格子数为偶数(\(0\)也算偶数)),这两个列条带就不用考虑了,其他的列考虑相邻的行都不能相同(否则就会出现横条带),发现一列就只有两种情况,\(010101010....\)和\(1010101010...\)但如果这一列中有数字的话,那么这一列就从这两种情况中唯一确定了。设\(nx\)为没有数字的列,那么列的情况数就是\(2^{nx}\),横条带和列类似。同时我们发现我们如果有某一行出现产生列条带的条件,这两列也被唯一确定了,同时他们也因为相对应的列出现了数字而被唯一确定,他们对答案并不影响。换句话说我们根本没有必要去统计到底是哪些列出现了产生列条带的条件,我们只关注到底有没有哪一行出现产生列条带的条件,从而使得整个局面处于列条带的情况下。

那么我们怎么快速知道一行是否有相邻且数字相同的格子出现,我们发现这和格子的奇偶有关,我们只需要记录每行,每列格子中数字0/1处在奇数,偶数的位置的个数,结合这个和上面的两条判断条带出现的条件,当一个新的数来时,我们可以快速判断这一行这一列是否产生条带。

具体我们需要维护一下信息:

1.每个格子的颜色。(用map<pair<int,int>,int>解决。)

2.哪些行产生条带,哪些列产生条带了。支持动态删除和加入,统计数量。(用set解决。)

3.哪些行,列出现了数字。支持动态删除,加入,统计数量。(用set结局。)

这里的小细节就是有时候统计答案,可能会重复计算没有条带的情况。这里特判下即可。

最后代码如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+10,P=998244353;
int n,m,k,cnth[N][2][2],cntl[N][2][2],cntx[2][2];
//cnth,cntl分别记录第i行/列数字0/1在奇数/偶数的位置上有多少个
//cntx这里是为了特判当横条带,列条带都没出现时,当前的数字能否确定出现没条带的情况。
ll p[N];//p预处理2的次幂
map<pair<int,int>,int>mp;//记录格子上的数字。
set<int>sh,sl;//记录哪些行/列能因此形成条带
set<int>uh,ul;//记录哪些行/列有数字
inline bool check(int cnt[2][2])//判断这个行/列是否能形成条带。
{
if(cnt[1][0]>0&&cnt[1][1]>0||cnt[0][0]>0&&cnt[0][1]>0) return true;
for(int i=0;i<=1;++i)
{
if(cnt[1][i]>0&&cnt[0][i]>0) return true;
}
return false;
}
inline void add(int x,int y,int t)//像某个格子添加数字
{
cnth[x][t][y&1]++;
cntl[y][t][x&1]++;
if(uh.find(x)==uh.end()) uh.insert(x);
if(ul.find(y)==ul.end()) ul.insert(y);
if(sh.find(x)==sh.end()&&check(cnth[x]))
sh.insert(x);
if(sl.find(y)==sl.end()&&check(cntl[y]))
sl.insert(y);
mp[{x,y}]=t;
cntx[t][(x&1)^(y&1)]++;
}
inline void upd(int x,int y,int t)
{
if(mp.find({x,y})!=mp.end())//先把这个格子的数删掉。
{
cntx[mp[{x,y}]][(x&1)^(y&1)]--;//先删除这个格子的影响,以及这个行,列所在的信息。
cnth[x][mp[{x,y}]][y&1]--;
cntl[y][mp[{x,y}]][x&1]--;
uh.erase(x);ul.erase(y);
if(sh.find(x)!=sh.end()) sh.erase(x);
if(sl.find(y)!=sl.end()) sl.erase(y);
mp.erase({x,y});
//以下重新统计该行该列的信息。
if(cnth[x][0][0]||cnth[x][0][1]||cnth[x][1][0]||cnth[x][1][1]) uh.insert(x);
if(cntl[y][0][0]||cntl[y][0][1]||cntl[y][1][0]||cntl[y][1][1]) ul.insert(y);
if(check(cnth[x])) sh.insert(x);
if(check(cntl[y])) sl.insert(y);
}
if(t==-1) return;
add(x,y,t);
return;
}
int main()
{
//freopen("1.in","r",stdin);
p[0]=1;
for(int i=1;i<=1e6;++i) p[i]=p[i-1]*2%P;
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=k;++i)
{
int x,y,t;
scanf("%d%d%d",&x,&y,&t);
upd(x,y,t);
ll ans=0;
if(sh.size()>0&&sl.size()>0) ans=0;//即出现横条带也出现列条带,答案为0.
else if(sh.size()>0) ans=p[m-ul.size()];//只出现列条带,按照列条带的方式计算。
else if(sl.size()>0) ans=p[n-uh.size()];//只出现横条带。
else
{
if(ul.size()==0&&uh.size()==0) ans=(p[n]+p[m]-2+P)%P;//两个都没出现。
else
{
ans=(p[m-ul.size()]+p[n-uh.size()]+P)%P;
if(cntx[1][0]+cntx[0][1]==0||cntx[0][0]+cntx[1][1]==0) ans=(ans-1+P)%P;
}
}
printf("%lld\n",ans);
}
return 0;
}

不用尽全力,你永远不会知道自己的潜力有多大!

Educational Codeforces Round 114 (Rated for Div. 2)题解的更多相关文章

  1. Educational Codeforces Round 63 (Rated for Div. 2) 题解

    Educational Codeforces Round 63 (Rated for Div. 2)题解 题目链接 A. Reverse a Substring 给出一个字符串,现在可以对这个字符串进 ...

  2. Educational Codeforces Round 65 (Rated for Div. 2)题解

    Educational Codeforces Round 65 (Rated for Div. 2)题解 题目链接 A. Telephone Number 水题,代码如下: Code #include ...

  3. Educational Codeforces Round 64 (Rated for Div. 2)题解

    Educational Codeforces Round 64 (Rated for Div. 2)题解 题目链接 A. Inscribed Figures 水题,但是坑了很多人.需要注意以下就是正方 ...

  4. Educational Codeforces Round 60 (Rated for Div. 2) 题解

    Educational Codeforces Round 60 (Rated for Div. 2) 题目链接:https://codeforces.com/contest/1117 A. Best ...

  5. Educational Codeforces Round 58 (Rated for Div. 2) 题解

    Educational Codeforces Round 58 (Rated for Div. 2)  题目总链接:https://codeforces.com/contest/1101 A. Min ...

  6. Educational Codeforces Round 47 (Rated for Div. 2) 题解

    题目链接:http://codeforces.com/contest/1009 A. Game Shopping 题目: 题意:有n件物品,你又m个钱包,每件物品的价格为ai,每个钱包里的前为bi.你 ...

  7. Educational Codeforces Round 93 (Rated for Div. 2)题解

    A. Bad Triangle 题目:https://codeforces.com/contest/1398/problem/A 题解:一道计算几何题,只要观察数组的第1,2,n个,判断他们能否构成三 ...

  8. Educational Codeforces Round 33 (Rated for Div. 2) 题解

    A.每个状态只有一种后续转移,判断每次转移是否都合法即可. #include <iostream> #include <cstdio> using namespace std; ...

  9. Educational Codeforces Round 78 (Rated for Div. 2) 题解

    Shuffle Hashing A and B Berry Jam Segment Tree Tests for problem D Cards Shuffle Hashing \[ Time Lim ...

随机推荐

  1. 自己实现Controller——标准型

    标准Controller 上一篇通过一个简单的例子,编写了一个controller-manager,以及一个极简单的controller.从而对controller的开发有个最基本的认识,但是细心观察 ...

  2. Kubernetes-Pod介绍(三)-Pod调度

    前言 本篇是Kubernetes第六篇,大家一定要把环境搭建起来,看是解决不了问题的,必须实战. Kubernetes系列文章: Kubernetes介绍 Kubernetes环境搭建 Kuberne ...

  3. JDBC-2(CRUD)

    3.PreparedStatement实现CRUD 3.1 操作和访问数据库 数据库连接被用于向数据库服务器发送命令和SQL语句,接受数据库服务器返回的结果.(一个数据库连接就是也给Socket连接) ...

  4. php 页面公共部分 转化为js document.write(); 并由匿名函数包裹

    页面公共部分以javascript  document.write()方式加载 生成的js放到需要的位置   footer.js 放到body底部引入 ... <script src=" ...

  5. javascript DOM 共同父节点

    * 查找两个节点的最近的一个共同父节点,可以包括节点自身 input: oNode1 和 oNode2 在同一文档中,且不会为相同的节点 function commonParentNode(oNode ...

  6. python学习笔记(九)-函数2

    交换两个变量的值 a = 2 b = 1 b = 1 a = 2 #方式一: b,a = a,b #交换两个变量的值 print(a,b) #方式二: a = a + b #3 b = a - b # ...

  7. YbtOJ#643-机器决斗【贪心,李超树】

    正题 题目链接:https://www.ybtoj.com.cn/problem/643 题目大意 \(n\)个机器人,第\(i\)个攻击力为\(A_i\),防御为\(D_i\). 然后你每次可以对一 ...

  8. P4643-[国家集训队]阿狸和桃子的游戏【结论】

    正题 题目链接:https://www.luogu.com.cn/problem/P4643 题目大意 给出\(n\)个点\(m\)条边的无向图,两个人轮流选择一个未被选择的点加入点集. 然后每个人的 ...

  9. self是什么?什么时候加?什么时候不加?

    Python里边self倒底是什么?什么时候加self?什么时候不加? self是什么? 如果你问别人大多人回答是: 指对象本身,然后噼里啪啦说一堆,然后听完的你,仍然完全搞不清楚,什么时候变量前需要 ...

  10. 【数据结构与算法】二叉树的 Morris 遍历(前序、中序、后序)

    前置说明 不了解二叉树非递归遍历的可以看我之前的文章[数据结构与算法]二叉树模板及例题 Morris 遍历 概述 Morris 遍历是一种遍历二叉树的方式,并且时间复杂度O(N),额外空间复杂度O(1 ...