2018 ACM-ICPC青岛现场赛 B题 Kawa Exam 题解 ZOJ 4059
题意:
BaoBao正在进行在线考试(都是选择题),每个题都有唯一的一个正确答案,但是考试系统有m个bug(就是有m个限制),每个bug表示为第u个问题和第v个问题你必须选择相同的选项,题目问你,如果你修好了第i个bug,BaoBao最高可以取得多少分。
题目数量1e5
BUG数量1e5(真多)
答案范围1e5
思路:首先,如果出现了bug,导致{a1,a2,...,an}n个题目必须选择一样的结果,那么最高得分肯定是众数的出现次数。我们发现bug是具有传递性的,如果bug连成了一个环,而且你只修复其中一个bug,那么这个bug的修复是对整个考试系统是没有任何影响的,所以我们不需要考虑这个环内的边,然后我们可以把这些环抠出来,缩成一个点,具体的缩点方法很多,也可以把整个块拉成一条链或者拓扑排序弄一弄,或者tarjan缩一下,我是用tarjan缩点的,这样整个图就是一个树了。
现在修一个bug就相当于断了一条边,一棵树就被切成两半了,我们需要同时得到两边的众数的个数是多少,这时候就有一个算法叫dsu on tree了,这是一个复杂度十分科学的暴力算法,http://codeforces.com/problemset/problem/600/E 这个题是一个dsu on tree的裸题,就是让你求每个子树的众数,不会这个算法的可以去学一下,做完这个题你就发现你会做子树内众数的个数了,现在还有个问题,子树外的怎么求呢?我们可以反过来做,一开始把所有的节点加进贡献里面去,然后dsu on tree的时候,询问子树众数的添加删除的时候,我们只要做相反的操作就行了。
具体细节还挺多的。
#include<bits/stdc++.h>
using namespace std;
const int maxn = ;struct Edge {
int to,next,id;
Edge(int _to=,int _next=-,int _id=):to(_to),next(_next),id(_id) {};
} edge[maxn*];
int head[maxn],etot;
inline void addedge(int u,int v,int id) {
edge[++etot]=Edge(v,head[u],id);
head[u]=etot;
}
vector<int> nodes[maxn];
int Cnt;
int dfn[maxn],low[maxn],tot;
bool Vis[maxn];
int S[maxn],top;
int id[maxn];
void tarjan(int x,int fa) {
low[x]=dfn[x]=++tot;
S[++top]=x;
Vis[x]=;
for(register int i=head[x]; ~i; i=edge[i].next) {
int v=edge[i].to;
if(v==fa) {fa=;continue;}
if(!dfn[v]) {
tarjan(v,x);
low[x]=min(low[x],low[v]);
} else if(Vis[v])
low[x]=min(low[x],dfn[v]);
}
if(low[x]==dfn[x]) {
Cnt++;
while() {
int now=S[top--];
Vis[now]=;
id[now]=Cnt;
nodes[Cnt].push_back(now);
if(now==x) break;
}
}
}
int a[maxn],ans[maxn];
vector<int>v[maxn];
vector<int>edg[maxn],eid[maxn];
int summ;
bool vis[maxn];
int sz[maxn],son[maxn];
vector<int>vv;
int getid(int x) {
return lower_bound(vv.begin(),vv.end(),x)-vv.begin()+;
}
void init(int now,int pre=-){
vis[now]=;
sz[now]=v[now].size();
for(int it:v[now])vv.push_back(a[it]);
for(int to:edg[now]){
if(to==pre)continue;
init(to,now);
sz[now]+=sz[to];
if(!son[now]||sz[to]>sz[son[now]])
son[now]=to;
}
}
int tp[maxn],tp2[maxn],cnt[maxn],cnt2[maxn],Max=,Max2=;
bool big[maxn];
void update(int nows,int pre,int val){
for(int now:v[nows]){
tp[cnt[a[now]]]--;
cnt[a[now]]+=val;
tp[cnt[a[now]]]++;
if(cnt[a[now]]>Max)
Max=cnt[a[now]];
if(!tp[Max])Max--;
tp2[cnt2[a[now]]]--;
cnt2[a[now]]-=val;
tp2[cnt2[a[now]]]++;
if(cnt2[a[now]]>Max2)
Max2=cnt2[a[now]];
if(!tp2[Max2])Max2--;
}
for(int to:edg[nows])
if(to!=pre&&big[to]==)
update(to,nows,val);
}
void update2(int nows,int pre,int val){
for(int now:v[nows]){
tp2[cnt2[a[now]]]--;
cnt2[a[now]]+=val;
tp2[cnt2[a[now]]]++;
if(cnt2[a[now]]>Max2)
Max2=cnt2[a[now]];
if(!tp2[Max2])Max2--;
}
}
int temp;
void dfs(int now,int pre=-,int kep=,int id=){
int tid=;
for(register int i=;i<edg[now].size();i++){
int to=edg[now][i];
if(to==son[now])tid=eid[now][i];
if(to==pre||to==son[now])continue;
dfs(to,now,,eid[now][i]);
}
if(son[now])
dfs(son[now],now,,tid),big[son[now]]=;
update(now,pre,);
ans[id]=Max+Max2-temp;
big[son[now]]=;
if(!kep)update(now,pre,-);
}
void init2(int now,int pre=-){
for(int it:v[now])a[it]=getid(a[it]);
update2(now,,);
for(int to:edg[now]){
if(to==pre)continue;
init2(to,now);
}
}
void solve(int now){
vv.clear();
init(now);
for(register int i=;i<=sz[now];i++)tp[i]=tp2[i]=cnt[i]=cnt2[i]=big[i]=;Max=Max2=;
sort(vv.begin(),vv.end());
vv.erase(unique(vv.begin(),vv.end()),vv.end());
init2(now);
summ+=Max2;
temp=Max2;
dfs(now);
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
int n,m;
scanf("%d%d",&n,&m);
for(register int i=;i<=n;i++)head[i]=-,dfn[i]=son[i]=vis[i]=Vis[i]=,v[i].clear(),edg[i].clear(),eid[i].clear();
etot=tot=Cnt=top=;
for(register int i=;i<=n;i++)scanf("%d",a+i);
for(register int i=;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
addedge(v,u,i);
addedge(u,v,i);
}
for(register int i=;i<=n;i++)if(!dfn[i])tarjan(i,);
for(register int i=;i<=n;i++)v[id[i]].push_back(i);
summ=;
for(register int i=;i<=n;i++){
for(register int j=head[i];~j;j=edge[j].next){
int u=id[i],to=id[edge[j].to];
if(u==to){
ans[edge[j].id]=;
continue;
}
edg[u].push_back(to);eid[u].push_back(edge[j].id);
}
}
for(register int i=;i<=Cnt;i++)
if(!vis[i])
solve(i);
for(register int i=;i<=m;i++){
if(i>)putchar(' ');
printf("%d",ans[i]+summ);
}
puts("");
}
return ;
}
2018 ACM-ICPC青岛现场赛 B题 Kawa Exam 题解 ZOJ 4059的更多相关文章
- hdu 4435 第37届ACM/ICPC天津现场赛E题
转载请注明出处,谢谢http://blog.csdn.net/ACM_cxlove?viewmode=contents by---cxlove 题目:给出N个城市,从1开始需要遍历所有点,选择一 ...
- 2013 ACM/ICPC 长沙现场赛 A题 - Alice's Print Service (ZOJ 3726)
Alice's Print Service Time Limit: 2 Seconds Memory Limit: 65536 KB Alice is providing print ser ...
- 2013 ACM/ICPC 长沙现场赛 C题 - Collision (ZOJ 3728)
Collision Time Limit: 2 Seconds Memory Limit: 65536 KB Special Judge There's a round medal ...
- 2018ACM/ICPC 青岛现场赛 E题 Plants vs. Zombies
题意: 你的房子在0点,1,2,3,...,n(n<=1e5)点每个点都有一颗高度为0的花,浇一次水花会长a[i]. 你有一个机器人刚开始在你家,最多走m步,每一步只能往前走或者往后走,每走到一 ...
- hdu 4432 第37届ACM/ICPC天津现场赛B题
题目大意就是找出n的约数,然后把约数在m进制下展开,各个数位的每一位平方求和,然后按m进制输出. 模拟即可 #include<cstdio> #include<iostream> ...
- 2016 年 ACM/ICPC 青岛区域赛 Problem C Pocky
昨晚乱入学弟的训练赛,想了一下这个题.推导的过程中,加深了对公理化的概率论理解.$\newcommand{\d}{\mathop{}\!\mathrm{d}}$ 解法一 考虑 $ d < L$ ...
- 2013 ACM/ICPC 长沙网络赛J题
题意:一个数列,给出这个数列中的某些位置的数,给出所有相邻的三个数字的和,数列头和尾处给出相邻两个数字的和.有若干次询问,每次问某一位置的数字的最大值. 分析:设数列为a1-an.首先通过相邻三个数字 ...
- 2013 ACM/ICPC 长春网络赛E题
题意:给出一个字符串,要从头.尾和中间找出三个完全相等的子串,这些串覆盖的区间互相不能有重叠部分.头.尾的串即为整个字符串的前缀和后缀.问这个相同的子串的最大长度是多少. 分析:利用KMP算法中的ne ...
- 2013 ACM/ICPC 长春网络赛F题
题意:两个人轮流说数字,第一个人可以说区间[1~k]中的一个,之后每次每人都可以说一个比前一个人所说数字大一点的数字,相邻两次数字只差在区间[1~k].谁先>=N,谁输.问最后是第一个人赢还是第 ...
随机推荐
- 类与对象 && 继承
以下是本人的对类与对象.继承的一些理解,如有错误之处万望谅解,如有朋友愿意指正,十分乐意,万分感谢! 类与对象 类与对象是学习编程的基础(大概吧),那么何为类?何为对象呢? 一.简 ...
- java消息服务学习之JMS高级特性
将介绍的内容是: 控制消息确认.为发送消息指定选项.创建临时目的地.使用JMS本地事务.异步发送消息 五个方面. 1.控制消息确认 在JMS消息得到确认之前,并不认为它已经成功使用.要成功使用消息,通 ...
- composer安装。
我们这里实在Windows下进行的安装.1.下载Composer安装包.网址:https://getcomposer.org/download/下载Composer-Setup.exe 安装文件.2. ...
- 在linux和windows用c++编写c接口的动态库
linux 动态的头文件api.h #ifndef _API_H #define _API_H #ifdef DLL_IMPLEMENT #define DLL_EXPORT extern " ...
- JavaScript 中的 FileReader
在不经过服务器的时候,本地预览照片,当确定以后再上传 它是H5提供的构造函数 用法: <input type='file'> <img src='' alt=''> <s ...
- webmagic爬虫抓取工作室成员博客
一.导入依赖 <!--webmagic依赖--> <dependency> <groupId>us.codecraft</groupId> <ar ...
- C# 数组与集合的区别
数组:声明数组的时候必须指定数组的长度.数组在内存中是连续的,索引速度很快,赋值和修改也很简单. 数组的容量是固定的,您只能一次获取或设置一个元素的值,而ArrayList或List的容量可根据需要自 ...
- js的eval代码快速解密
有一段js代码内容如下: eval(function(E,I,A,D,J,K,L,H){function C(A)后面内容省略... 解密可以采用如下方法: 方法一: 打开谷歌浏览器,按F12,在Co ...
- 论文笔记:Fast Online Object Tracking and Segmentation: A Unifying Approach
Fast Online Object Tracking and Segmentation: A Unifying Approach CVPR-2019 2019-03-11 23:45:12 Pape ...
- 什么时候使用“RCC_APBXPeriph_AFIO”
什么时候需要开启复用时钟: (1)使用EXTI (2)重映射(用到外设的重映射功能时才需要使能AFIO的时钟) 举例:重映射USART2 USART2的TX/RX在PA.2/3.但是,PA.2已经被T ...