Educational Codeforces Round 114 (Rated for Div. 2)题解
还是常规的过了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)题解的更多相关文章
- Educational Codeforces Round 63 (Rated for Div. 2) 题解
Educational Codeforces Round 63 (Rated for Div. 2)题解 题目链接 A. Reverse a Substring 给出一个字符串,现在可以对这个字符串进 ...
- Educational Codeforces Round 65 (Rated for Div. 2)题解
Educational Codeforces Round 65 (Rated for Div. 2)题解 题目链接 A. Telephone Number 水题,代码如下: Code #include ...
- Educational Codeforces Round 64 (Rated for Div. 2)题解
Educational Codeforces Round 64 (Rated for Div. 2)题解 题目链接 A. Inscribed Figures 水题,但是坑了很多人.需要注意以下就是正方 ...
- Educational Codeforces Round 60 (Rated for Div. 2) 题解
Educational Codeforces Round 60 (Rated for Div. 2) 题目链接:https://codeforces.com/contest/1117 A. Best ...
- Educational Codeforces Round 58 (Rated for Div. 2) 题解
Educational Codeforces Round 58 (Rated for Div. 2) 题目总链接:https://codeforces.com/contest/1101 A. Min ...
- Educational Codeforces Round 47 (Rated for Div. 2) 题解
题目链接:http://codeforces.com/contest/1009 A. Game Shopping 题目: 题意:有n件物品,你又m个钱包,每件物品的价格为ai,每个钱包里的前为bi.你 ...
- Educational Codeforces Round 93 (Rated for Div. 2)题解
A. Bad Triangle 题目:https://codeforces.com/contest/1398/problem/A 题解:一道计算几何题,只要观察数组的第1,2,n个,判断他们能否构成三 ...
- Educational Codeforces Round 33 (Rated for Div. 2) 题解
A.每个状态只有一种后续转移,判断每次转移是否都合法即可. #include <iostream> #include <cstdio> using namespace std; ...
- 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 ...
随机推荐
- Docker入门系列之二:Docker术语
原文作者:Jeff Hale 原文地址:https://towardsdatascience.com/learn-enough-docker-to-be-useful-1c40ea269fa8 翻译: ...
- Spring Cloud Hystrix 学习(二)熔断与降级
今天来看下Hystrix的熔断与降级. 首先什么是降级?当请求超时.资源不足等情况发生时进行服务降级处理,不调用真实服务逻辑,而是使用快速失败(fallback)方式直接返回一个托底数据,保证服务链条 ...
- Nginx系列(2)- 正向代理和反向代理
Nginx作用 Http代理,反向代理:作为web服务器最常用的功能之一,尤其是反向代理 正向代理是代理客户端,反向代理是代理服务端 正向代理要知道访问服务器的地址,反向代理不需要知道访问服务器的真实 ...
- Shell系列(1)- Shell概述
Shell是什么 Shell是一个命令行解释器,它为用户提供了一个向Linux内核发送请求以便运行程序的界面系统级程序,用户可以用Shell来启动.挂起.停止甚至时编写一些程序 Shell还是一个功能 ...
- ubuntu提示:无法获得锁 /var/lib/dpkg/lock-frontend - open (11: 资源暂时不可用)
root@uni-virtual-machine:/home/uni# apt install apt-transport-https ca-certificates curl software-pr ...
- 为Python安装Redis库
为Python安装Redis库,登陆https://github.com/andymccurdy/redis-py 后点击Download ZIP下载安装包. 解压并安装: git clone htt ...
- 一文让你彻底理解group by和聚合函数
知道group by是进行分组查询,但是一直觉得对其理解得不够透彻,在网上扒了一篇文章,我认为写得非常好. 为什么不能够select * from Table group by id,为什么一定不能是 ...
- bzoj4589-Hard Nim【FWT】
正题 题目链接:https://darkbzoj.tk/problem/4589 题目大意 求有多少个长度为\(n\)的数列满足它们都是不大于\(m\)的质数且异或和为\(0\). 解题思路 两个初始 ...
- Docker部署Mysql,如何开启binlog
0.拉取镜像 sudo docker pull mysql:5.7 1.创建存放映射文件夹 mkdir -p mydata/mysql/log mkdir -p mydata/mysql/data m ...
- nodejs 安装 报错解决方案
win10安装nodejs之后,查看版本号在终端输入node -v成功输出版本号,输入npm -v 之后报错...... 反复安装卸载之后,有点奔溃,最后的解决方案是:手动删除"C:\Use ...