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 ...
随机推荐
- Java比较两个浮点数
浮点数的基本数据类型不能用==比较,包装数据类型不能用 equals 比较 浮点数的表示 在计算机系统中,浮点数采用 符号+阶码+尾数 进行表示.在Java中,单精度浮点数float类型占32位,它的 ...
- 学习PHP中Fileinfo扩展的使用
今天来学习的这个扩展其实现在也已经是标配的一个扩展了,为什么呢?因为 Laravel 框架在安装的时候它就是必须的一个扩展,没有打开它的话,连 Laravel 框架都是无法使用的. Fileinfo ...
- PHP中命名空间是怎样的存在(一)?
命名空间其实早在PHP5.3就已经出现了.不过大部分同学可能在各种框架的使用中才会接触到命名空间的内容,当然,现代化的开发也都离不开这些能够快速产出的框架.这次我们不从框架的角度,仅从简单的代码角度来 ...
- 深入剖析RocketMQ源码-NameServer
一.RocketMQ架构简介 1.1 逻辑部署图 (图片来自网络) 1.2 核心组件说明 通过上图可以看到,RocketMQ的核心组件主要包括4个,分别是NameServer.Broker.Produ ...
- PKI及SSL协议分析PKI及SSL协议分析
任务一:搭建CA服务器 本任务初步了解CA服务器的原理和配置过程.操作都在CA服务器上. 1.远程桌面方式登录到CA服务器,在CMD下查看本机IP地址: 2.安装证书服务 依次点击:"开始& ...
- HBase基础
Hadoop生态系统 HBase简介 HBase – Hadoop Database,是一个高可靠性.高性能.面向列.可伸缩.实时读写的分布式数据库 利用Hadoop HDFS作为其文件存储系统,利用 ...
- oracle查看和修改session和最大连接数
第一步,在cmd命令行,输入sqlplus 第二步,根据提示输入用户名与密码 1. 查看processes和sessions参数 SQL> show parameter processes NA ...
- SONiC架构分析
目录 系统架构 设计原则 核心组件 SWSS 容器 syncd 容器 网络应用容器 内部通信模型 SubscriberStateTable NotificationProducer/Consumer ...
- codeforces316E3 Summer Homework(线段树,斐波那契数列)
题目大意 给定一个n个数的数列,m个操作,有三种操作: \(1\ x\ v\) 将\(a_x\)的值修改成v $2\ l\ r\ $ 求 \(\sum_{i=l}^r x_i*f_{i-l}\) 其中 ...
- java设计模式_单例模式
懒汉式 非线程安全 特点:Lazy 初始化.非多线程安全.易实现 描述:这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程.因为没有加锁 synchronized,所以严格意义上它并不算单 ...