[POI 2008]Mafia

这题目写了我好长时间,但还是几乎(不要在意细节)一遍 A 了喵~

据说有代码奇短的,Orz 思路巧妙的大爷

想我这种 Brute Force 写写的傻 X 真是代码量飞起来了耶,喵~

如果把每个人看成点,每个人要 kill 的人向此人连出一条有向边,那么每个点仅有一条出边和一条入边

经验告诉我们,这就是 环+内向图 的节奏

经验又告诉我,处理这种图要么先暴力搞环,再搞挂在环上的树。要么先搞树,再弄环。

此题显然是后者

环+内向图只需要用 bfs 就可以搞出来了,看到写 tarjan 的真是 Orz

弄出 环 和 树 后,先在树上跑 dp,用 dp[u][0] 表示如果 u 最后被 kill 了,那么以 u 为根子树最少要死几人, dp[u][1] 是 u 存活下来的情况,这是普及组难度的树形dp

再在环上跑 dp ,我们先破环为链,则有3种情况 (我们令在首的人为 A , 在尾的人为 B)

1. A君 最后活着,那 B君 一定被 kill 了

2. A君 最后被 kill 了,B君 存活了或也被 kill 了

3. A君 存活了下来,B君 也存活了下来,然后 B君 kill A君

三种情况要分类讨论

中间过程的状态转移和以上三种情况类似,无非就是讨论 i 君 和 i+1 君 的是否被 kill 的关系  (妈妈说,某个字打出来是不好的喵~)

被 kill 有两种可能:1.被自己的子树中的某君 kill 了  2.被环上的某君 kill 了

这个 dp 也很好想嘛

似乎除了难写就没有难度了喵?

  1 #include <cstdio>
  2 #include <cstring>
  3 const int size=1000001;
  4 const int inf=size;
  5
  6 namespace IOspace
  7 {
  8     inline void assign() {freopen("isaac.in", "r", stdin); freopen("isaac.out", "w", stdout);}
  9     inline void close() {fclose(stdin); fclose(stdout);}
 10     inline int getint()
 11     {
 12         register int num=0;
 13         register char ch;
 14         do ch=getchar(); while (ch<‘0‘ || ch>‘9‘);
 15         do num=num*10+ch-‘0‘, ch=getchar(); while (ch>=‘0‘ && ch<=‘9‘);
 16         return num;
 17     }
 18     inline void putint(int num, char ch=‘\n‘)
 19     {
 20         char stack[15];
 21         register int top=0;
 22         if (num==0) stack[top=1]=‘0‘;
 23         for ( ;num;num/=10) stack[++top]=num%10+‘0‘;
 24         for ( ;top;top--) putchar(stack[top]);
 25         if (ch) putchar(ch);
 26     }
 27 }
 28
 29 int n;
 30 int g[size][2];
 31 int t, a[size];
 32 int f[size], d[size];
 33 int dp[size][2];
 34 bool vis[size], loop[size];
 35
 36 struct edge {int point; edge * next;};
 37 edge MEM[size], * PORT=MEM;
 38 edge * E[size];
 39 inline edge * newedge(int _point, edge * _next)
 40     {edge * ret=PORT++; ret->point=_point; ret->next=_next; return ret;}
 41
 42 inline int min(int x, int y) {return x<y?x:y;}
 43 inline int max(int x, int y) {return x>y?x:y;}
 44 inline void add(int & x, int y) {if (x>=inf || y>=inf) x=inf; else x+=y;}
 45 inline int search(int);
 46 inline int find(int);
 47 inline void bfs(int);
 48
 49 int main()
 50 {
 51     int ans1=0, ans2=0;
 52
 53     n=IOspace::getint();
 54     for (int i=1;i<=n;i++)
 55     {
 56         f[i]=IOspace::getint(), d[f[i]]++;
 57         if (f[i]==i) ans2++, vis[i]=1;
 58         E[f[i]]=newedge(i, E[f[i]]);
 59     }
 60
 61     for (int i=1;i<=n;i++) if (!d[i])
 62     {
 63         vis[i]=1;
 64         ans2+=search(i);
 65     }
 66     for (int i=1;i<=n;i++) if (!vis[i])
 67     {
 68         vis[i]=1;
 69         ans2+=search(i);
 70     }
 71
 72     memset(vis, 0, sizeof(vis));
 73     for (int i=1;i<=n;i++) if (!vis[i])
 74     {
 75         vis[i]=1;
 76         ans1+=find(i);
 77     }
 78
 79     IOspace::putint(ans1, ‘ ‘); IOspace::putint(ans2);
 80
 81     return 0;
 82 }
 83 inline int search(int x)
 84 {
 85     int ret=0;
 86     for (x=f[x];!vis[x];x=f[x]) vis[x]=1, ret++;
 87     return ret;
 88 }
 89 inline void bfs(int x)
 90 {
 91     static int l, r, q[size];
 92
 93     l=r=0;
 94     for (q[r++]=x;l<r; )
 95     {
 96         int u=q[l++];
 97         for (edge * i=E[u];i;i=i->next)
 98             if (!loop[i->point])
 99             {
100                 vis[i->point]=1;
101                 q[r++]=i->point;
102             }
103     }
104
105     for (int i=r-1;i>=0;i--)
106         if (E[q[i]]==NULL) dp[q[i]][0]=inf;
107         else if (E[q[i]]->next==NULL && loop[E[q[i]]->point]) dp[q[i]][0]=((E[q[i]]->point)==q[i])?1:inf;
108         else
109         {
110             for (edge * j=E[q[i]];j;j=j->next)
111                 if (!loop[j->point])
112                     add(dp[q[i]][1], dp[j->point][0]);
113             for (edge * j=E[q[i]];j;j=j->next)
114                 if (!loop[j->point])
115                     add(dp[q[i]][0], min(dp[j->point][0], dp[j->point][1]));
116             add(dp[q[i]][0], 1);
117         }
118 }
119 inline int find(int x)
120 {
121     int ret=0;
122
123     vis[x]=1;
124     for (x=f[x];!vis[x];x=f[x]) vis[x]=1;
125     for (t=0;!loop[x];x=f[x]) loop[a[t++]=x]=1;
126     for (int i=0;i<t;i++) bfs(a[i]);
127     if (t==1) return dp[a[0]][0];
128     g[0][1]=dp[a[0]][1]; g[0][0]=inf;
129     for (int i=1;i<t;i++)
130     {
131         g[i][0]=min(g[i-1][0], g[i-1][1]);
132         add(g[i][0], min(dp[a[i]][0], dp[a[i]][1]+1));
133         g[i][1]=g[i-1][0];
134         add(g[i][1], dp[a[i]][1]);
135     }
136     ret=g[t-1][0];
137
138     g[0][0]=dp[a[0]][0]; g[0][1]=inf;
139     for (int i=1;i<t;i++)
140     {
141         g[i][0]=min(g[i-1][0], g[i-1][1]);
142         add(g[i][0], min(dp[a[i]][0], dp[a[i]][1]+1));
143         g[i][1]=g[i-1][0];
144         add(g[i][1], dp[a[i]][1]);
145     }
146     ret=min(ret, min(g[t-1][0], g[t-1][1]));
147
148     g[0][0]=dp[a[0]][1]+1; g[0][1]=inf;
149     for (int i=1;i<t;i++)
150     {
151         g[i][0]=min(g[i-1][0], g[i-1][1]);
152         add(g[i][0], min(dp[a[i]][0], dp[a[i]][1]+1));
153         g[i][1]=g[i-1][0];
154         add(g[i][1], dp[a[i]][1]);
155     }
156     ret=min(ret, g[t-1][1]);
157
158     return ret;
159 }

因为是考试时写的所以很长也是没办法的事系列

[POI 2008]Mafia

时间: 2024-08-23 20:17:29

[POI 2008]Mafia的相关文章

[POI 2008][BZOJ 1132]Tro

这题我真是无能为力了 这题的做法还是挺简单的 枚举左下角的点做为原点,把其余点按极角排序    PS.是作为原点,如枚举到 k 时,对于所有 p[i] (包括p[k]) p[i]-=p[k] (此处为向量减法) 排序后满足 i<j 的两个向量 p[i] 和 p[j] 的叉积都是正数了 ΣΣp[i]×p[j] = ΣΣ(p[i].x*p[j].y-p[i].y*p[j].x) = Σ(p[i].x*Σp[j].y)-Σ(p[i].y*Σp[j].x) 计算叉积和的复杂度就从 O(n2) 降为了 O

BZOJ 1132 POI 2008 Tro 计算几何

题目大意:给出平面上的一些点,问这些点中的任意三个点组成的三角形的面积和是多少. 思路:看数据范围只算法系列.由于每个三角形有三个顶点,因此暴力的话应该是O(n^3)的时间复杂度,很明显超时了,但是我们只需要将它优化到O(n^2logn)就可以解决了. 好吧,剩下的随便猜一猜,比如O(n^2)的枚举,然后剩下的logn什么也干不了... 再比如O(n)的枚举,然后剩下O(nlogn)排序... 好像有戏啊.. 枚举每一个点,计算以这个点为坐标原点,在第一象限的所有点与原点组成的三角形的面积和.计

BZOJ 1339 Baltic 2008 Mafia 最小点割集

题目大意:一个城市中有些点,有一些双向道路将这些点连接起来,每个点都有权值,求警察最少占据的点的权值和使得从A点无法到达B点. 思路:最小点割集签到题. CODE: #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 80010 #define INF 0x3f3f3f3f using

[POI 2008&amp;洛谷P3467]PLA-Postering题解(单调栈)

Description Byteburg市东边的建筑都是以旧结构形式建造的:建筑互相紧挨着,之间没有空间.它们共同形成了一条长长的,从东向西延伸的建筑物链(建筑物的高度不一). Byteburg市的市长Byteasar,决定将这个建筑物链的一侧用海报覆盖住.并且想用最少的海报数量,海报是矩形的. 海报与海报之间不能重叠,但是可以相互挨着(即它们具有公共边),每一个海报都必须贴近墙并且建筑物链的整个一侧必须被覆盖(意思是:海报需要将一侧全部覆盖,并且不能超出建筑物链) 输入格式:第一行为一个整数n

bzoj 1112 poi 2008 砖块

这滞胀题调了两天了... 好愚蠢的错误啊... 其实这道题思维比较简单,就是利用treap进行维护(有人说线段树好写,表示treap真心很模板) 就是枚举所有长度为k的区间,查出中位数,计算代价即可. (根据绝对值不等式的几何意义,中位数一定是最优解) 而维护长度为k的区间也很简单,就是首先把前k个扔到树上,然后每次把新来的插入,把最前面的一个删除即可 至于求中位数,简直就是基础操作嘛 关键在于...代价怎么算? 显然我们不能把所有数枚举出来挨个加减,这样会T飞的... 所以我们考虑直接在tre

解题:POI 2008 Subdivision of Kingdom

题面 还可以这么搜......学到了(PoPoQQQ orz) 我们最朴素的做法是枚举所有状态(当然可以剪,剪完最终实际状态量也是$C_{26}^{13}$的),然后每次$O(n)$扫一遍判断,大概会T炸,考虑优化 我们先预处理每个状态中$1$的数目和连边的状态,然后压缩状态初始让一边集合为空,一边集合为全集,这样每次从已有的点的前面$\frac{n}{2}$个点中枚举一个加入另一边,就可以边搜边更新边数而不用最后$O(n)$检查了.另一个问题是数组可能非常大,这里我们可以把状态拆成前后两半,然

浅谈单调栈的实现方式和简单应用

一.单调栈的原理和实现方式 1.定义 从栈底元素到栈顶元素呈单调递增或单调递减,栈内序列满足单调性的栈: 2.原理 (1)当新元素在单调性上优于栈顶时(单增栈新元素比栈顶大,单减栈新元素比栈顶小),压栈,栈深+1: (2)当新元素在单调性与栈顶相同(新元素于栈顶相同)或劣于栈顶时(单增栈新元素比栈顶小,单减栈新元素比栈顶大),弹栈,栈深-1: 3.一般实现形式 以单增栈(栈顶为最大值)为例: n为元素数,h为入栈序列,tot为栈深,stack为单增栈: void stacks(){ int st

Java POI Word 写文档

package apache.poi; import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStream;import java.util.Ha

java使用poi解析或处理excel的时候,如何防止数字变成科学计数法的形式和其他常见Excel中数据转换问题

当使用POI处理excel的时候,遇到了比较长的数字,虽然excel里面设置该单元格是文本类型的,但是POI的cell的类型就会变成数字类型. 而且无论数字是否小数,使用cell.getNumbericCellValue() 去获取值的时候,会得到一个double,而且当长度大一点的时候会变成科学计数法形式. 那么获取这个单元格的原始的数据,就其实是一个double怎么转换成整数的问题了. 使用DecimalFormat对这个double进行了格式话,随后使用format方法获得的String就