10.11-16的模拟赛
时间:2010-11-16 来源:LitIce
【问题描述】
有n个炸弹,有些炸弹牵了一根单向引线(也就是说引线只有在这一端能被炸弹点燃),只要引爆了这个炸弹,用引线连接的下一个炸弹也会爆炸。每个炸弹还有个得分,当这个炸弹被引爆后就能得到相应得分。
现在要你引爆k个炸弹,使得分最大。
【输入格式】
第1行两个整数n、k。
接下来n行每行两个整数a[i]、b[i]。a[i]表示这个炸弹用引线连接的下一个炸弹,如果a[i]为0,则表示这个炸弹没连接引线。b[i]表示这个炸弹的得分。
【输出格式】
仅一个数,表示最大得分。
【输入样例】bomb.in
8 2
0 1
1 1
2 100
2 100
0 1
5 1
6 2
6 2
【输出样例】bomb.out
202
【数据规模】
1≤b[i]≤1000000
对于30%的数据,n≤1000 k≤30
对于60%的数据,n≤50000 k≤100
对于100%的数据,n≤200000 k≤500
--------------------------------------------------------------------------------------------
考试的时候没想出来T T。
Algorithm:
先缩环成点,这样就变成了一棵树。(题目特殊可以dfs缩点,代码里还是打了tarjan)
贪心取出一条最大链,删掉后再重复这个过程。(不用真的删除只要Dp时处理一下就可以了)
最后排序所有取出的链,排序取前m大。

1 #include<iostream>
2 #include<cstdio>
3 #define N 300001
4 #define LL long long
5 using namespace std;
6 struct node{
7 LL y,next;
8 } E[N];
9
10 LL DFN[N],Low[N],S[N],inS[N],col,color[N],edg[N],f[N];
11 LL index,Ecnt,n,m,x;
12 LL a[N],c[N],b[N],aa[N],cc,bo[N],cnt,vis[N],root,ans;
13 void add_edge(LL x,LL y)
14 {
15 ++Ecnt;E[Ecnt].y=y;E[Ecnt].next=edg[x];edg[x]=Ecnt;
16 }
17 void tarjan(LL u)
18 {
19 DFN[u]=Low[u]=++index;S[++cnt]=u;inS[u]=1;
20
21 LL v;
22 for (LL j=edg[u];j!=0;j=E[j].next)
23 {
24 v=E[j].y;
25 if (DFN[v]==0)
26 {
27 tarjan(v);
28 if (Low[v]<Low[u]) Low[u]=Low[v];
29
30 }
31 else
32 if (inS[v]==1 && Low[v]<Low[u]) Low[u]=Low[v];
33 }
34
35 if (DFN[u]==Low[u])
36 {
37 ++col;
38 while (cnt>0 && Low[S[cnt]]==DFN[u])
39 {
40 color[S[cnt]]=col;
41 --cnt;
42 }
43 }
44 }
45 LL dp(LL u)
46 {
47 if (bo[u]) return f[u];
48 bo[u]=1;
49
50 LL v,Big=0,bi;
51 for (LL j=edg[u];j!=0;j=E[j].next)
52 {
53 v=E[j].y;
54 if (dp(v)>Big) Big=dp(v),bi=v;
55 }
56 f[u]=Big+b[u];
57 for (LL j=edg[u];j!=0;j=E[j].next)
58 {
59 if (E[j].y!=bi) vis[E[j].y]=1;
60 }
61 return f[u];
62 }
63 int main()
64 {
65 freopen("bomb.in","r",stdin);
66 freopen("bomb.out","w",stdout);
67 scanf("%d%d",&n,&m);
68 for (LL i=1;i<=n;++i)
69 {
70 scanf("%d%d",&x,&c[i]);
71 a[i]=x;
72 add_edge(x,i);
73 }
74 for (LL i=0;i<=n;++i)
75 if (DFN[i]==0) tarjan(i);
76 // for (LL i=0;i<=n;++i)
77 // printf("%d %d\n",i,color[i]);
78 Ecnt=0;for (LL i=1;i<=col;++i) edg[i]=0;
79
80 for (LL i=1;i<=n;++i)
81 if (color[a[i]]!=color[i])
82 add_edge(color[a[i]],color[i]);
83
84 for (LL i=1;i<=n;++i)
85 b[color[i]]+=c[i];
86
87 root=color[0];
88
89 for (LL i=1;i<=col;++i) bo[i]=0;
90 vis[root]=1;
91 dp(root);
92
93 cc=0;
94 for (LL i=1;i<=col;++i) if (vis[i])
95 {
96 ++cc;
97 aa[cc]=f[i];
98 }
99 sort(aa+1,aa+cc);
100 while (cc>0 && m>0)
101 {
102 ans+=aa[cc];
103 --cc;--m;
104 }
105 cout<<ans<<endl;
106 return 0;
107 }
108
---------------------------------------------------------------------------------------------
【问题描述】
有若干个圆饼套在一根柱子里,形成了一个汉诺塔(圆饼从上到下依次编号为1~p),我们想把汉诺塔从0号柱子移到n+1号柱子,中间有n个柱子和m块空地给我们使用。移动规则如下:
- 从0号柱子移出的圆饼不能移回0号柱子,移到n+1号柱子的圆饼不能再移动。
- I号圆饼只能放在I+1号圆饼上、空地上和柱子最下面。
- 空地上只能放一个圆饼,而柱子上可以按规则2形成汉诺塔。
- 可以直接将圆饼从0号柱子移到n+1号柱子。
现在我们有n个柱子和m块空地可以使用,问你最多可以将多高的汉诺塔从0号柱子移到n+1号柱子(高度是指形成汉诺塔的圆饼数目)。由于高度可能很大,所以只需你求出高度mod k的值。
【输入格式】
只有一行,为三个正整数n,m,k,三个数分别用空格隔开。
【输出格式】
只有一个数,为最大高度mod k的值。
【输入样例】tower.in
1 1 10
【输出样例】tower.out
4
【数据规模】
对于50%的数据n,m,k<=103
对于80%的数据n,m,k<=106
对于100%的数据n,m,k<=109
---------------------------------------------------------------------------------------------
推出公式:ans=2^n*(m+1)% k;
先不考虑空地。假设空地为数为0
手动算出
F(0)=1 (直接从0->n+1)
F(1)=2
F(2)=4
就可以预测F(3)=8
0 1 2 3 4
从0->1通过2,3最多可移动 4个
从0->2通过3最多可移动 2个
从0->3 最多可移动1个
从0->4 移动1个
3->4 移动1个
2->4 通过 3 移动2个
1->4 通过 2 3 移动4 个。
这样就完成了8个的移动
于是F(n)>=2^n;
现在证明F(n)<=2^n
F(0),F(1),F(2)满足条件
因为最大的那个盘要移动0->n+1缩以中间的所有柱子都只能够先粗村
F(3)时比F(2)多了一根柱子储存,他最多只能储存F(2)个盘子,
故F(3)<=2*F(2);命题得证
现在再考虑空地 空地就相当于吧每一次移动一个盘变成移动m+1个盘
(显然我们可以先把m+1个中的m个先分散到空地,第m+1个移动到目标后,再将m个
移动过去)