[关闭]
@ivorysi 2018-01-02T09:03:58.000000Z 字数 32423 阅读 693

常用算法2学习笔记

笔记


01分数规划

POJ 2976 Dropping tests

Description

In a certain course, you take n tests. If you get ai out of bi questions correct on test i, your cumulative average is defined to be.
Given your test scores and a positive integer k, determine how high you can make your cumulative average if you are allowed to drop any k of your test scores.
Suppose you take 3 tests with scores of 5/5, 0/1, and 2/6. Without dropping any tests, your cumulative average is . However, if you drop the third test, your cumulative average becomes .

Input

The input test file will contain multiple test cases, each containing exactly three lines. The first line contains two integers, 1 ≤ n ≤ 1000 and 0 ≤ k < n. The second line contains n integers indicating ai for all i. The third line contains n positive integers indicating bi for all i. It is guaranteed that 0 ≤ ai ≤ bi ≤ 1, 000, 000, 000. The end-of-file is marked by a test case with n = k = 0 and should not be processed.

Output

For each test case, write a single line with the highest cumulative average possible after dropping k of the given test scores. The average should be rounded to the nearest integer.

Sample Input

3 1
5 0 2
5 1 6
4 2
1 2 7 9
5 6 7 9
0 0

Sample Output

83
100

Hint

To avoid ambiguities due to rounding errors, the judge tests have been constructed so that all answers are at least 0.001 away from a decision boundary (i.e., you can assume that the average is never 83.4997).

题解

01分数规划的板子题
我们枚举一个最大的分数

的时候说明l可以取到或者更大
说明l取不到

代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cmath>
  5. #include <cstring>
  6. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  7. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  8. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  9. #define MAXN 1005
  10. //#define ivorysi
  11. using namespace std;
  12. typedef long long ll;
  13. int n,k;
  14. double a[MAXN],b[MAXN],pos[MAXN];
  15. bool check(double mid) {
  16. siji(i,1,n) pos[i]=a[i]-mid*b[i];
  17. sort(pos+1,pos+n+1);
  18. double res=0;
  19. gongzi(i,n,k+1) {
  20. res+=pos[i];
  21. }
  22. return res>=0.0;
  23. }
  24. int main() {
  25. #ifdef ivorysi
  26. freopen("f1.in","r",stdin);
  27. #endif
  28. while(scanf("%d%d",&n,&k)!=EOF) {
  29. siji(i,1,n) scanf("%lf",&a[i]);
  30. siji(i,1,n) scanf("%lf",&b[i]);
  31. if(n+k==0) break;
  32. double l=0.0,r=1.0;
  33. int num=100;
  34. while(num--) {
  35. double mid=(l+r)/2.0;
  36. if(check(mid)) l=mid;
  37. else r=mid;
  38. }
  39. l=l*100;
  40. printf("%d\n",(int)(l+0.5));
  41. }
  42. return 0;
  43. }

POJ 2728 Desert King

Description

David the Great has just become the king of a desert country. To win the respect of his people, he decided to build channels all over his country to bring water to every village. Villages which are connected to his capital village will be watered. As the dominate ruler and the symbol of wisdom in the country, he needs to build the channels in a most elegant way.
After days of study, he finally figured his plan out. He wanted the average cost of each mile of the channels to be minimized. In other words, the ratio of the overall cost of the channels to the total length must be minimized. He just needs to build the necessary channels to bring water to all the villages, which means there will be only one way to connect each village to the capital.
His engineers surveyed the country and recorded the position and altitude of each village. All the channels must go straight between two villages and be built horizontally. Since every two villages are at different altitudes, they concluded that each channel between two villages needed a vertical water lifter, which can lift water up or let water flow down. The length of the channel is the horizontal distance between the two villages. The cost of the channel is the height of the lifter. You should notice that each village is at a different altitude, and different channels can't share a lifter. Channels can intersect safely and no three villages are on the same line.
As King David's prime scientist and programmer, you are asked to find out the best solution to build the channels.

Input

There are several test cases. Each test case starts with a line containing a number N (2 <= N <= 1000), which is the number of villages. Each of the following N lines contains three integers, x, y and z (0 <= x, y < 10000, 0 <= z < 10000000). (x, y) is the position of the village and z is the altitude. The first village is the capital. A test case with N = 0 ends the input, and should not be processed.

Output

For each test case, output one line containing a decimal number, which is the minimum ratio of overall cost of the channels to the total length. This number should be rounded three digits after the decimal point.

Sample Input

4
0 0 0
0 1 1
1 1 2
1 0 3
0

Sample Output

1.000

题解

这道题是最小比率生成树,必须用prim算法,限制二分枚举次数的时候需要注意不能太大……大概50次应该够
二分上界是所有点到原点的高度之和,这个一定覆盖上界
然后每次枚举一个平均,每条边的边权是:价值-平均值×距离,然后做最小生成树

代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cmath>
  5. #include <cstring>
  6. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  7. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  8. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  9. #define MAXN 1005
  10. //#define ivorysi
  11. using namespace std;
  12. typedef long long ll;
  13. int n,tot;
  14. double x[MAXN],y[MAXN],z[MAXN],dis[MAXN];
  15. double mul(double x) {return x*x;}
  16. int fa[MAXN];
  17. bool vis[MAXN];
  18. int getfa(int x) {
  19. return fa[x]==x ? x : fa[x]=getfa(fa[x]);
  20. }
  21. double dist(int a,int b) {
  22. return sqrt(mul(x[a]-x[b])+mul(y[a]-y[b]));
  23. }
  24. bool check(double mid) {
  25. memset(vis,0,sizeof(vis));
  26. siji(i,2,n) dis[i]=fabs(z[1]-z[i])-mid*dist(1,i);
  27. vis[1]=1;
  28. int tot=n-1;
  29. int id=-1;
  30. double val=0.0,tmp=0.0;
  31. while(tot--) {
  32. id=-1;
  33. siji(i,1,n) {
  34. if(!vis[i]) {
  35. if(id==-1) {id=i;}
  36. else if(dis[id]>dis[i]) {id=i;}
  37. }
  38. }
  39. tmp+=dis[id];
  40. vis[id]=1;
  41. siji(i,1,n) {
  42. if(!vis[i]) {
  43. dis[i]=min(dis[i],fabs(z[i]-z[id])-mid*dist(i,id));
  44. }
  45. }
  46. }
  47. return tmp<=0.0;
  48. }
  49. int main() {
  50. #ifdef ivorysi
  51. freopen("f1.in","r",stdin);
  52. #endif
  53. while(scanf("%d",&n)!=EOF) {
  54. if(!n) break;
  55. int num=50;
  56. siji(i,1,n) scanf("%lf%lf%lf",&x[i],&y[i],&z[i]);
  57. double l=0.0,r=0.0;
  58. siji(j,2,n) {
  59. r+=fabs(z[j]-z[1]);
  60. }
  61. while(num--) {
  62. double mid=(l+r)/2.0;
  63. if(check(mid)) r=mid;
  64. else l=mid;
  65. }
  66. printf("%.3lf\n",r);
  67. }
  68. return 0;
  69. }

POJ 3621 Sightseeing Cows

Description

Farmer John has decided to reward his cows for their hard work by taking them on a tour of the big city! The cows must decide how best to spend their free time.
Fortunately, they have a detailed city map showing the L (2 ≤ L ≤ 1000) major landmarks (conveniently numbered 1.. L) and the P (2 ≤ P ≤ 5000) unidirectional cow paths that join them. Farmer John will drive the cows to a starting landmark of their choice, from which they will walk along the cow paths to a series of other landmarks, ending back at their starting landmark where Farmer John will pick them up and take them back to the farm. Because space in the city is at a premium, the cow paths are very narrow and so travel along each cow path is only allowed in one fixed direction.
While the cows may spend as much time as they like in the city, they do tend to get bored easily. Visiting each new landmark is fun, but walking between them takes time. The cows know the exact fun values Fi (1 ≤ Fi ≤ 1000) for each landmark i.
The cows also know about the cowpaths. Cowpath i connects landmark L1i to L2i (in the direction L1i -> L2i ) and requires time Ti (1 ≤ Ti ≤ 1000) to traverse.
In order to have the best possible day off, the cows want to maximize the average fun value per unit time of their trip. Of course, the landmarks are only fun the first time they are visited; the cows may pass through the landmark more than once, but they do not perceive its fun value again. Furthermore, Farmer John is making the cows visit at least two landmarks, so that they get some exercise during their day off.
Help the cows find the maximum fun value per unit time that they can achieve.

Input

  • Line 1: Two space-separated integers: L and P
  • Lines 2..L+1: Line i+1 contains a single one integer: Fi
  • Lines L+2..L+P+1: Line L+i+1 describes cow path i with three space-separated integers: L1i , L2i , and Ti

Output

  • Line 1: A single number given to two decimal places (do not perform explicit rounding), the maximum possible average fun per unit time, or 0 if the cows cannot plan any trip at all in accordance with the above rules.

Sample Input

5 7
30
10
10
5
10
1 2 3
2 3 2
3 4 5
3 5 2
4 5 5
5 1 3
5 2 2

Sample Output

6.00

题解

这道题是最小比率生成环
也是二分一个小数,然后修改边权,spfa上跑负环
每条边的边权变成:比率×边权-边起点的点权(这个是分数式的取值取负,因为要跑负环)
把每条边的dis设成0,然后跑spfa,因为反正都要找负环,就把每个点都当成起点,一定会有一条路从开始走回来都是负的,所以我们只把距离从0往小里更新
用spfa跑dfs如果有重复计算的点那么必然是有负环,这样保证了点不会被重复计算

代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cmath>
  5. #include <cstring>
  6. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  7. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  8. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  9. #define MAXN 1005
  10. //#define ivorysi
  11. using namespace std;
  12. typedef long long ll;
  13. int L,P;
  14. struct node {
  15. int to,next,val;
  16. }edge[5005];
  17. int head[1005],sumedge;
  18. double fun[1005],dis[1005];
  19. bool vis[1005];
  20. void add(int u,int v,int c) {
  21. edge[++sumedge].to=v;
  22. edge[sumedge].next=head[u];
  23. edge[sumedge].val=c;
  24. head[u]=sumedge;
  25. }
  26. bool spfa_dfs(int u,double w) {
  27. vis[u]=1;
  28. for(int i=head[u];i;i=edge[i].next) {
  29. int v=edge[i].to;
  30. if(dis[v] > dis[u]+edge[i].val*w-fun[u]) {
  31. dis[v]=dis[u]+edge[i].val*w-fun[u];
  32. if(!vis[v]) {if(spfa_dfs(v,w)) return 1;}
  33. else return 1;
  34. }
  35. }
  36. vis[u]=0;
  37. return 0;
  38. }
  39. bool check(double mid) {
  40. memset(vis,0,sizeof(vis));
  41. memset(dis,0,sizeof(dis));
  42. siji(i,1,L) {
  43. if(spfa_dfs(i,mid)) return 1;
  44. }
  45. return 0;
  46. }
  47. int main() {
  48. #ifdef ivorysi
  49. freopen("f1.in","r",stdin);
  50. #endif
  51. scanf("%d%d",&L,&P);
  52. double l=0.0,r=0.0;
  53. siji(i,1,L) {scanf("%lf",&fun[i]); r+=fun[i];}
  54. int u,v,c;
  55. siji(i,1,P) {
  56. scanf("%d%d%d",&u,&v,&c);
  57. add(u,v,c);
  58. }
  59. int num=50;
  60. while(num--) {
  61. double mid=(l+r)/2.0;
  62. if(check(mid)) l=mid;
  63. else r=mid;
  64. }
  65. printf("%.2f\n",l);
  66. return 0;
  67. }

POJ 3155 Hard Life

Description

John is a Chief Executive Officer at a privately owned medium size company. The owner of the company has decided to make his son Scott a manager in the company. John fears that the owner will ultimately give CEO position to Scott if he does well on his new manager position, so he decided to make Scott’s life as hard as possible by carefully selecting the team he is going to manage in the company.
John knows which pairs of his people work poorly in the same team. John introduced a hardness factor of a team — it is a number of pairs of people from this team who work poorly in the same team divided by the total number of people in the team. The larger is the hardness factor, the harder is this team to manage. John wants to find a group of people in the company that are hardest to manage and make it Scott’s team. Please, help him.
In the example on the picture the hardest team consists of people 1, 2, 4, and 5. Among 4 of them 5 pairs work poorly in the same team, thus hardness factor is equal to 5⁄4. If we add person number 3 to the team then hardness factor decreases to 6⁄5.

Input

The first line of the input file contains two integer numbers n and m (1 ≤ n ≤ 100, 0 ≤ m ≤ 1000). Here n is a total number of people in the company (people are numbered from 1 to n), and m is the number of pairs of people who work poorly in the same team. Next m lines describe those pairs with two integer numbers ai and bi (1 ≤ ai, bi ≤ n, ai ≠ bi) on a line. The order of people in a pair is arbitrary and no pair is listed twice.

Output

Write to the output file an integer number k (1 ≤ k ≤ n) — the number of people in the hardest team, followed by k lines listing people from this team in ascending order. If there are multiple teams with the same hardness factor then write any one.

Sample Input

sample input #1
5 6
1 5
5 4
4 2
2 5
1 2
3 1
sample input #2
4 0

Sample Output

sample output #1
4
1
2
4
5
sample output #2
1
1

Hint

Note, that in the last example any team has hardness factor of zero, and any non-empty list of people is a valid answer.

题解

再复习一下最大密度子图
首先我们需要知道最大闭合权子图
最大闭合权子图就是选取一个点集,这些点集的出边指向的点也在这个集合里,要求点权和最大,点权有正有负
做法是把每条边的边权改成正无穷,原点向点权为正的点连一条容量为点权的边,点权为负的点向汇点连一条容量为点权绝对值的边,然后跑网络流,所有点权为正的点的和减去最大流就是能得到的最大值,然后在残余网络里dfs能搜到的点就是在点集里的点

最大密度子图是

我们把边抽象成一个点,选了一条边要选对应的两个点,每个点的点权是-g,每个边抽象成的点的点权是1,然后跑最大权闭合子图,看是不是>0,g就是合法的

代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cmath>
  5. #include <cstring>
  6. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  7. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  8. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  9. #define MAXN 1005
  10. #define eps 1e-8
  11. //#define ivorysi
  12. using namespace std;
  13. typedef long long ll;
  14. struct node {
  15. int to,next;
  16. double val;
  17. }edge[100005];
  18. int head[2005],sumedge,source,sink,dis[2005],gap[2005];
  19. int n,m,ans;
  20. bool vis[2005];
  21. struct data {
  22. int a,b;
  23. }Eg[2005];
  24. void add(int u,int v,double c) {
  25. edge[++sumedge].to=v;
  26. edge[sumedge].next=head[u];
  27. edge[sumedge].val=c;
  28. head[u]=sumedge;
  29. }
  30. void addtwo(int u,int v,double c) {
  31. add(u,v,c);add(v,u,0);
  32. }
  33. double sap(int u,double aug) {
  34. if(u==sink) return aug;
  35. int dmin=sink-1;
  36. double flow=0;
  37. for(int i=head[u];i;i=edge[i].next) {
  38. if(edge[i].val>0) {
  39. int v=edge[i].to;
  40. if(dis[v]+1==dis[u]) {
  41. double t=sap(v,min(edge[i].val,aug-flow));
  42. flow+=t;
  43. edge[i].val-=t;
  44. edge[i^1].val+=t;
  45. if(aug-flow<eps) return flow;
  46. if(dis[source]>=sink) return flow;
  47. }
  48. dmin=min(dmin,dis[v]);
  49. }
  50. }
  51. if(!flow) {
  52. --gap[dis[u]];
  53. if(gap[dis[u]]==0) dis[source]=sink;
  54. dis[u]=dmin+1;
  55. ++gap[dis[u]];
  56. }
  57. return flow;
  58. }
  59. bool check(double mid) {
  60. memset(head,0,sizeof(head));sumedge=1;
  61. memset(dis,0,sizeof(dis));
  62. memset(gap,0,sizeof(gap));
  63. siji(i,1,m) {
  64. addtwo(i+n+1,Eg[i].a+1,(double)m);
  65. addtwo(i+n+1,Eg[i].b+1,(double)m);
  66. addtwo(source,i+n+1,1);
  67. }
  68. siji(i,1,n) addtwo(i+1,sink,mid);
  69. double ans=m;
  70. while(dis[source]<sink) ans-=sap(source,ans);
  71. return ans > eps;
  72. }
  73. void dfs(int u) {
  74. vis[u]=1;
  75. for(int i=head[u];i;i=edge[i].next) {
  76. if(edge[i].val>0) {
  77. int v=edge[i].to;
  78. if(!vis[v]) dfs(v);
  79. }
  80. }
  81. }
  82. int main() {
  83. #ifdef ivorysi
  84. freopen("f1.in","r",stdin);
  85. #endif
  86. scanf("%d%d",&n,&m);
  87. siji(i,1,m) {
  88. scanf("%d%d",&Eg[i].a,&Eg[i].b);
  89. }
  90. if(m==0) {
  91. printf("1\n1\n");
  92. return 0;
  93. }
  94. source=1,sink=m+n+2;
  95. double l=0,r=m;
  96. int num=100;
  97. while(num--) {
  98. double mid=(l+r)/2.0;
  99. if(check(mid)) l=mid;
  100. else r=mid;
  101. }
  102. check(l);
  103. dfs(source);
  104. siji(i,1,n) if(vis[i+1]) ++ans;
  105. printf("%d\n",ans);
  106. siji(i,1,n) if(vis[i+1]) printf("%d\n",i);
  107. return 0;
  108. }

高斯消元

BZOJ 1013: [JSOI2008]球形空间产生器sphere

Description

有一个球形空间产生器能够在n维空间中产生一个坚硬的球体。现在,你被困在了这个n维球体中,你只知道球
面上n+1个点的坐标,你需要以最快的速度确定这个n维球体的球心坐标,以便于摧毁这个球形空间产生器。

Input

第一行是一个整数n(1<=N=10)。接下来的n+1行,每行有n个实数,表示球面上一点的n维坐标。每一个实数精确到小数点
后6位,且其绝对值都不超过20000。

Output

有且只有一行,依次给出球心的n维坐标(n个实数),两个实数之间用一个空格隔开。每个实数精确到小数点
后3位。数据保证有解。你的答案必须和标准输出一模一样才能够得分。

Sample Input

2
0.0 0.0
-1.0 1.0
1.0 0.0

Sample Output

0.500 1.500

HINT

提示:给出两个定义:1、 球心:到球面上任意一点距离都相等的点。2、 距离:设两个n为空间上的点A, B的坐标为(a1, a2, …, an), (b1, b2, …, bn),则AB的距离定义为:dist = sqrt( (a1-b1)^2 + (a2-b2)^2 +… + (an-bn)^2 )

题解

高斯消元,注意交换的时候要一直交换到n+1……

代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cmath>
  5. #include <cstring>
  6. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  7. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  8. #define sigongzi(j,x,y) for(int j=(x);j>(y);--j)
  9. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  10. //#define ivorysi
  11. using namespace std;
  12. typedef long long ll;
  13. int n,m;
  14. double c[15][15];
  15. double f[15][15];
  16. double ans[15];
  17. void Guass() {
  18. siji(i,1,n) {
  19. int l=i;
  20. siji(j,l+1,n) if(fabs(f[j][i])>fabs(f[l][i])) l=j;
  21. if(l!=i) {
  22. siji(j,i,m) swap(f[i][j],f[l][j]);//这里是m不是n……交换一直连值也交换
  23. }
  24. siji(j,i+1,n) {
  25. double tmp=f[j][i]/f[i][i];
  26. siji(k,i,m) {
  27. f[j][k]-=f[i][k]*tmp;
  28. }
  29. }
  30. }
  31. gongzi(i,n,1) {
  32. double t=f[i][m];
  33. sigongzi(j,n,i) t-=ans[j]*f[i][j];
  34. ans[i]=t/f[i][i];
  35. }
  36. }
  37. int main() {
  38. #ifdef ivorysi
  39. freopen("f1.in","r",stdin);
  40. #endif
  41. scanf("%d",&n);m=n+1;
  42. siji(i,0,n) {
  43. siji(j,1,n) {
  44. scanf("%lf",&c[i][j]);
  45. }
  46. }
  47. siji(i,1,n) {
  48. double d=0;
  49. siji(j,1,n) {
  50. f[i][j]=2*(c[i][j]-c[i-1][j]);
  51. d+=(c[i][j]*c[i][j]-c[i-1][j]*c[i-1][j]);
  52. }
  53. f[i][m]=d;
  54. }
  55. Guass();
  56. siji(i,1,n) {
  57. printf("%.3lf%c",ans[i]," \n"[i==n]);
  58. }
  59. return 0;
  60. }

BZOJ 2337: [HNOI2011]XOR和路径

题面

给出一张无向连通图,有重边和自环,计算1到n的路径的异或和的期望,在每个点等概率选择一条路去走

题解

因为是异或,异或对每一个二进制位影响都是独立的,所以拆位来做
我们列出期望的dp来
表示从x到n该位是1的概率
把边权按这一位是否是1转化成1或0

这样如果是0的话那么就取如果是1的话就取减少了讨论情况
如果y是n的话,
但是这又不是一张拓扑图,没有办法dp,只能高斯消元,每次统计上1到n的概率乘上位权

代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cmath>
  5. #include <cstring>
  6. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  7. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  8. #define sigongzi(j,x,y) for(int j=(x);j>(y);--j)
  9. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  10. //#define ivorysi
  11. using namespace std;
  12. typedef long long ll;
  13. int n,m;
  14. struct node {
  15. int to,next,val;
  16. }edge[1000005];
  17. int head[105],sumedge,deg[105];
  18. double f[105][105],ans[105];
  19. void add(int u,int v,int c) {
  20. edge[++sumedge].to=v;
  21. edge[sumedge].val=c;
  22. edge[sumedge].next=head[u];
  23. head[u]=sumedge;
  24. ++deg[u];
  25. }
  26. void Guass() {
  27. memset(ans,0,sizeof(ans));
  28. for(int i=1;i<=n-1;++i) {
  29. int l=i;
  30. for(int j=l+1;j<=n-1;++j) if(fabs(f[j][i])>fabs(f[l][i])) l=j;
  31. if(l!=i) {
  32. for(int j=i;j<=n;++j) swap(f[i][j],f[l][j]);
  33. }
  34. for(int j=i+1;j<=n-1;++j) {
  35. double tmp=f[j][i]/f[i][i];
  36. for(int k=i;k<=n;++k) {
  37. f[j][k]-=f[i][k]*tmp;
  38. }
  39. }
  40. }
  41. for(int i=n-1;i>=1;--i) {
  42. double t=f[i][n];
  43. for(int j=n-1;j>i;--j) t-=ans[j]*f[i][j];
  44. ans[i]=t/f[i][i];
  45. }
  46. }
  47. int main() {
  48. #ifdef ivorysi
  49. freopen("f1.in","r",stdin);
  50. #endif
  51. scanf("%d%d",&n,&m);
  52. int u,v,c;
  53. siji(i,1,m) {
  54. scanf("%d%d%d",&u,&v,&c);
  55. add(u,v,c);
  56. if(u!=v) add(v,u,c);
  57. }
  58. double res=0.0;
  59. siji(i,0,30) {
  60. memset(f,0,sizeof(f));
  61. siji(u,1,n-1) {
  62. f[u][u]=1;
  63. for(int k=head[u];k;k=edge[k].next) {
  64. int v=edge[k].to,w=edge[k].val;
  65. w=(w>>i)&1;
  66. if(v!=n) {
  67. f[u][v]+=(double)(2*w-1)/deg[u];
  68. f[u][n]+=(double)w/deg[u];
  69. }
  70. else f[u][n]+=(double)w/deg[u];
  71. }
  72. }
  73. Guass();
  74. res+=ans[1]*(1<<i);
  75. }
  76. printf("%.3lf\n",res);
  77. return 0;
  78. }

BZOJ 3143: [Hnoi2013]游走

Description

一个无向连通图,顶点从1编号到N,边从1编号到M。
小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。

Input

第一行是正整数N和M,分别表示该图的顶点数 和边数,接下来M行每行是整数u,v(1≤u,v≤N),表示顶点u与顶点v之间存在一条边。 输入保证30%的数据满足N≤10,100%的数据满足2≤N≤500且是一个无向简单连通图。

Output

仅包含一个实数,表示最小的期望值,保留3位小数。

Sample Input

3 3
2 3
1 2
1 3

Sample Output

3.333

HINT

边(1,2)编号为1,边(1,3)编号2,边(2,3)编号为3。

题解

如果我们能求出每条边的期望经过次数,就可以贪心地把编号赋给他们,然而问题来了,我们怎么求出每条边期望经过次数
我们转化成求每个点经过的次数,或者说是每个点能做决策的次数
那么每条边期望经过的次数就是

走到n点就不动弹了,所以n点能做决策的次数是0,这个比较特殊

每个点的x是

会得到n-1个方程
然后高斯消元……

为什么除掉删边操作忽然就A了不T了……

代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cmath>
  5. #include <cstring>
  6. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  7. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  8. #define sigongzi(j,x,y) for(int j=(x);j>(y);--j)
  9. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  10. //#define ivorysi
  11. using namespace std;
  12. typedef long long ll;
  13. int n,m;
  14. int deg[505],U[1000005],V[1000005];
  15. double f[505][505],ans[505],ex[1000005];
  16. void Guass() {
  17. for(int i=1;i<=n-1;++i) {
  18. int l=i;
  19. for(int j=l+1;j<=n-1;++j) if(fabs(f[j][i])>fabs(f[l][i])) l=j;
  20. if(l!=i) {
  21. for(int j=i;j<=n;++j) swap(f[i][j],f[l][j]);
  22. }
  23. for(int j=i+1;j<=n-1;++j) {
  24. double tmp=f[j][i]/f[i][i];
  25. for(int k=i;k<=n;++k) {
  26. f[j][k]-=tmp*f[i][k];
  27. }
  28. }
  29. }
  30. for(int i=n-1;i>=1;--i) {
  31. double t=f[i][n];
  32. for(int j=n-1;j>i;--j) t-=ans[j]*f[i][j];
  33. ans[i]=t/f[i][i];
  34. }
  35. }
  36. int main() {
  37. #ifdef ivorysi
  38. freopen("f1.in","r",stdin);
  39. #endif
  40. scanf("%d%d",&n,&m);
  41. int u,v;
  42. siji(i,1,m) {
  43. scanf("%d%d",&u,&v);
  44. deg[u]++;deg[v]++;
  45. U[i]=u;V[i]=v;
  46. }
  47. siji(i,1,n-1) f[i][i]=-1.0;
  48. siji(i,1,m) {
  49. if(U[i]!=n && V[i]!=n) {
  50. f[V[i]][U[i]]=1.0/deg[U[i]];
  51. f[U[i]][V[i]]=1.0/deg[V[i]];
  52. }
  53. }
  54. f[1][n]=-1.0;
  55. Guass();
  56. siji(i,1,m) {
  57. ex[i]=ans[U[i]]/deg[U[i]]+ans[V[i]]/deg[V[i]];
  58. }
  59. sort(ex+1,ex+m+1);
  60. double res=0;
  61. gongzi(i,m,1) {
  62. res+=ex[m-i+1]*i;
  63. }
  64. printf("%.3lf\n",res);
  65. return 0;
  66. }

BZOJ 3572: [Hnoi2014]世界树

Description

世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界。在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息、持续运转的根本基石。
世界树的形态可以用一个数学模型来描述:世界树中有n个种族,种族的编号分别从1到n,分别生活在编号为1到n的聚居地上,种族的编号与其聚居地的编号相同。有的聚居地之间有双向的道路相连,道路的长度为1。保证连接的方式会形成一棵树结构,即所有的聚居地之间可以互相到达,并且不会出现环。定义两个聚居地之间的距离为连接他们的道路的长度;例如,若聚居地a和b之间有道路,b和c之间有道路,因为每条道路长度为1而且又不可能出现环,所卧a与c之间的距离为2。
出于对公平的考虑,第i年,世界树的国王需要授权m[i]个种族的聚居地为临时议事处。对于某个种族x(x为种族的编号),如果距离该种族最近的临时议事处为y(y为议事处所在聚居地的编号),则种族x将接受y议事处的管辖(如果有多个临时议事处到该聚居地的距离一样,则y为其中编号最小的临时议事处)。
现在国王想知道,在q年的时间里,每一年完成授权后,当年每个临时议事处将会管理多少个种族(议事处所在的聚居地也将接受该议事处管理)。 现在这个任务交给了以智慧著称的灵长类的你:程序猿。请帮国王完成这个任务吧。

Input

第一行为一个正整数n,表示世界树中种族的个数。
接下来n-l行,每行两个正整数x,y,表示x聚居地与y聚居地之间有一条长度为1的双
向道路。接下来一行为一个正整数q,表示国王询问的年数。
接下来q块,每块两行:
第i块的第一行为1个正整数m[i],表示第i年授权的临时议事处的个数。
第i块的第二行为m[i]个正整数h[l]、h[2]、…、h[m[i]],表示被授权为临时议事处的聚居地编号(保证互不相同)。

Output

输出包含q行,第i行为m[i]个整数,该行的第j(j=1,2…,,m[i])个数表示第i年被授权的聚居地h[j]的临时议事处管理的种族个数。

Sample Input

10
2 1
3 2
4 3
5 4
6 1
7 3
8 3
9 4
10 1
5
2
6 1
5
2 7 3 6 9
1
8
4
8 7 10 3
5
2 9 3 5 8

Sample Output

1 9
3 1 4 1 1
10
1 1 3 5
4 1 3 1 1

HINT

N<=300000, q<=300000,m[1]+m[2]+…+m[q]<=300000

题解

这是虚树(思想在那虚树路径上彷徨)
我们把这些特殊点选出来作为一棵树,构建方法用一个栈维护右链,新加入的一个点和栈顶的点求lca,如果lca不是栈里的点,就把lca也加进去
求的方法可以在虚树上做这样的操作

1.首先,虚树里最高的点,它以上的点一定和它的控制点相同
2.每个虚树上的点必然会和它在虚树上的点共用一个控制点
3.如果u和u的父亲fa共用一个控制点,那么u到fa的路径上的点以及这些点的子树也是用这个控制点
4.如果不用一个控制点,那么这条路径上一定存在一个分界点,使得前面路径的点属于fa的控制点,后面路径属于u的控制点,这个就是,u到控制点的距离+fa到控制点的距离+两点之间的距离的一半
然而会有距离相等然而fa控制点的编号较小,这个时候我们再往下走一步

代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cmath>
  5. #include <cstring>
  6. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  7. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  8. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  9. #define pii pair<int,int>
  10. #define fi first
  11. #define se second
  12. #define INF 0x3f3f3f3f
  13. //#define ivorysi
  14. using namespace std;
  15. typedef long long ll;
  16. const int N=300005;
  17. const int M=20;
  18. int n,m,sol[N],ans[N];
  19. int fa[N][M],dep[N],dfn[N],size[N],inx;
  20. int num,aux[N],faAux[N],delta[N],val[N];
  21. pii f[N];
  22. int stk[N],top;
  23. struct node {
  24. int to,next;
  25. }edge[N*4];
  26. int head[N],sumedge;
  27. void add(int u,int v) {
  28. edge[++sumedge].to=v;
  29. edge[sumedge].next=head[u];
  30. head[u]=sumedge;
  31. }
  32. void addtwo(int u,int v) {
  33. add(u,v);add(v,u);
  34. }
  35. void build_tree(int u) {
  36. size[u]=1;
  37. dfn[u]=++inx;
  38. for(int i=head[u];i;i=edge[i].next) {
  39. int v=edge[i].to;
  40. if(v!=fa[u][0]) {
  41. fa[v][0]=u;
  42. dep[v]=dep[u]+1;
  43. build_tree(v);
  44. size[u]+=size[v];
  45. }
  46. }
  47. }
  48. bool cmp(int a,int b) {
  49. return dfn[a]<dfn[b];
  50. }
  51. int lca(int a,int b) {
  52. int l=18;
  53. if(dep[a]<dep[b]) swap(a,b);
  54. while(l>=0) {
  55. if(dep[fa[a][l]]>=dep[b]) {
  56. a=fa[a][l];
  57. }
  58. --l;
  59. }
  60. if(a==b) return a;
  61. l=18;
  62. while(fa[a][0]!=fa[b][0]) {
  63. if(fa[a][l]!=fa[b][l]) {a=fa[a][l];b=fa[b][l];}
  64. --l;
  65. }
  66. return fa[a][0];
  67. }
  68. int Jump(int u,int d) {
  69. for(int l=0;d && l<=18;++l) {
  70. if(d&1) u=fa[u][l];
  71. d>>=1;
  72. }
  73. return u;
  74. }
  75. void build_auxtree(int aN) {
  76. num=aN;
  77. sort(aux+1,aux+num+1,cmp);
  78. stk[top=0]=0;
  79. siji(i,1,aN) {
  80. int u=aux[i];
  81. if(!top) {
  82. faAux[u]=0;stk[++top]=u;
  83. }
  84. else {
  85. int Lca=lca(stk[top],u);
  86. while(dep[stk[top]]>dep[Lca]) {
  87. if(dep[stk[top-1]]<=dep[Lca]) {
  88. faAux[stk[top]]=Lca;
  89. }
  90. --top;
  91. }
  92. if(stk[top]!=Lca) {
  93. faAux[Lca]=stk[top];
  94. f[Lca]=make_pair(INF,0);
  95. aux[++num]=Lca;
  96. stk[++top]=Lca;
  97. }
  98. faAux[u]=Lca;
  99. stk[++top]=u;
  100. }
  101. }
  102. sort(aux+1,aux+num+1,cmp);
  103. }
  104. void solve() {
  105. gongzi(i,num,2) {
  106. int u=aux[i];int v=faAux[u];
  107. delta[u]=dep[u]-dep[v];
  108. pii tmp=make_pair(f[u].fi+delta[u],f[u].se);
  109. if(tmp<f[v]) f[v]=tmp;
  110. }
  111. siji(i,2,num) {
  112. int u=aux[i];int v=faAux[u];
  113. pii tmp=make_pair(f[v].fi+delta[u],f[v].se);
  114. if(tmp<f[u]) f[u]=tmp;
  115. }
  116. siji(i,1,num) {
  117. int u=aux[i];int v=faAux[u];
  118. val[u]=size[u];
  119. if(i==1) {
  120. ans[f[u].se]+=n-size[u];
  121. continue;
  122. }
  123. int son=Jump(u,dep[u]-dep[v]-1);
  124. int calc=size[son]-size[u];
  125. val[v]-=size[son];
  126. if(f[u].se==f[v].se) {ans[f[u].se]+=calc;}
  127. else {
  128. int z=f[u].fi-f[v].fi+dep[u]+dep[v]+1 >>1;
  129. if(f[v].se < f[u].se && dep[u]-z+f[u].fi == z-dep[v]+f[v].fi) {
  130. ++z;
  131. }
  132. z=size[Jump(u,dep[u]-z)]-size[u];
  133. ans[f[u].se]+=z;
  134. ans[f[v].se]+=calc-z;
  135. }
  136. }
  137. siji(i,1,num) {
  138. ans[f[aux[i]].se]+=val[aux[i]];
  139. }
  140. }
  141. int main() {
  142. #ifdef ivorysi
  143. freopen("f1.in","r",stdin);
  144. #endif
  145. scanf("%d",&n);
  146. int u,v;
  147. xiaosiji(i,1,n) {
  148. scanf("%d%d",&u,&v);
  149. addtwo(u,v);
  150. }
  151. dep[1]=1;
  152. build_tree(1);
  153. siji(j,1,18) {
  154. siji(i,1,n) {
  155. fa[i][j]=fa[fa[i][j-1]][j-1];
  156. }
  157. }
  158. int Q;
  159. scanf("%d",&Q);
  160. while(Q--) {
  161. scanf("%d",&m);
  162. int u;
  163. siji(i,1,m) {
  164. scanf("%d",&u);
  165. f[u]=make_pair(0,u);
  166. aux[i]=u;
  167. sol[i]=u;
  168. ans[u]=0;
  169. }
  170. build_auxtree(m);
  171. solve();
  172. siji(i,1,m) {
  173. printf("%d ",ans[sol[i]]);
  174. }
  175. putchar('\n');
  176. }
  177. return 0;
  178. }

BZOJ 2286: [Sdoi2011]消耗战

Description

在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。
侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。

Input

第一行一个整数n,代表岛屿数量。
接下来n-1行,每行三个整数u,v,w,代表u号岛屿和v号岛屿由一条代价为c的桥梁直接相连,保证1<=u,v<=n且1<=c<=100000。
第n+1行,一个整数m,代表敌方机器能使用的次数。
接下来m行,每行一个整数ki,代表第i次后,有ki个岛屿资源丰富,接下来k个整数h1,h2,…hk,表示资源丰富岛屿的编号。

Output

输出有m行,分别代表每次任务的最小代价。

Sample Input

10
1 5 13
1 9 6
2 1 19
2 4 8
2 3 91
5 6 8
7 5 4
7 8 31
10 7 9
3
2 10 6
4 5 7 8 3
3 9 4 6

Sample Output

12
32
22

HINT

对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1

题解

虚树题其实都非常简单,只要把虚树建出来然后树dp就好
这道题我们先建一棵虚树,然后dp转移就是
如果它的儿子是一个有资源的节点,那么我们找到他到儿子的路径上边权最小的一条边(这个可以用倍增求lca的相同方式来维护)
如果它的儿子不是一个有资源的节点,那么我们找到儿子路径上边权最小的一条边删掉或者使用它儿子全部删除危险节点的费用

代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cmath>
  5. #include <cstring>
  6. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  7. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  8. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  9. #define pii pair<int,int>
  10. #define fi first
  11. #define se second
  12. #define INF 0x3f3f3f3f
  13. //#define ivorysi
  14. using namespace std;
  15. typedef long long ll;
  16. const int N=250005;
  17. int n,Q,m;
  18. struct node {
  19. int to,next,val;
  20. }edge[N*4];
  21. int head[N],sumedge;
  22. int fa[N][21],val[N][21],dfn[N],inx,size[N],dep[N];
  23. int sol[N],aux[N],faAux[N],num;
  24. bool vis[N];
  25. int stk[N],top;
  26. ll dp[N];
  27. void add(int u,int v,int c) {
  28. edge[++sumedge].to=v;
  29. edge[sumedge].next=head[u];
  30. edge[sumedge].val=c;
  31. head[u]=sumedge;
  32. }
  33. void addtwo(int u,int v,int c) {
  34. add(u,v,c);add(v,u,c);
  35. }
  36. void build_tree(int u) {
  37. dfn[u]=++inx;
  38. size[u]=1;
  39. for(int i=head[u];i;i=edge[i].next) {
  40. int v=edge[i].to;
  41. if(v!=fa[u][0]) {
  42. fa[v][0]=u;
  43. val[v][0]=edge[i].val;
  44. dep[v]=dep[u]+1;
  45. build_tree(v);
  46. size[u]+=size[v];
  47. }
  48. }
  49. }
  50. int lca(int a,int b) {
  51. int l=20;
  52. if(dep[a]<dep[b])swap(a,b);
  53. while(l>=0) {
  54. if(dep[fa[a][l]]>=dep[b]) a=fa[a][l];
  55. --l;
  56. }
  57. l=20;
  58. if(a==b) return a;
  59. while(fa[a][0]!=fa[b][0]) {
  60. if(fa[a][l]!=fa[b][l]) a=fa[a][l],b=fa[b][l];
  61. --l;
  62. }
  63. return fa[a][0];
  64. }
  65. bool cmp(int a,int b) {
  66. return dfn[a]<dfn[b];
  67. }
  68. int getdist(int u,int d) {
  69. int res=0x7fffffff;
  70. for(int l=0;d && l<=20;++l) {
  71. if(d&1) {res=min(res,val[u][l]);u=fa[u][l];}
  72. d>>=1;
  73. }
  74. return res;
  75. }
  76. void build_auxtree(int aN) {
  77. num=aN;
  78. sort(aux+1,aux+num+1,cmp);
  79. stk[top=0]=0;
  80. siji(i,1,aN) {
  81. int u=aux[i];
  82. if(!top) {
  83. stk[++top]=u;
  84. faAux[u]=0;
  85. }
  86. else {
  87. int Lca=lca(stk[top],u);
  88. while(dep[stk[top]]>dep[Lca]) {
  89. if(dep[stk[top-1]]<=dep[Lca]) {
  90. faAux[stk[top]]=Lca;
  91. }
  92. --top;
  93. }
  94. if(Lca!=stk[top]) {
  95. faAux[Lca]=stk[top];
  96. aux[++num]=Lca;
  97. stk[++top]=Lca;
  98. }
  99. stk[++top]=u;
  100. faAux[u]=Lca;
  101. }
  102. }
  103. sort(aux+1,aux+num+1,cmp);
  104. }
  105. void solve() {
  106. siji(i,1,num) dp[aux[i]]=0;
  107. gongzi(i,num,1) {
  108. int u=aux[i],v=faAux[u];
  109. if(!vis[u]) {
  110. dp[v]+=min(dp[u],(ll)getdist(u,dep[u]-dep[v]));
  111. }
  112. else {
  113. dp[v]+=getdist(u,dep[u]-dep[v]);
  114. }
  115. }
  116. }
  117. int main() {
  118. #ifdef ivorysi
  119. freopen("f1.in","r",stdin);
  120. #endif
  121. scanf("%d",&n);
  122. int u,v,c;
  123. xiaosiji(i,1,n) {
  124. scanf("%d%d%d",&u,&v,&c);
  125. addtwo(u,v,c);
  126. }
  127. dep[1]=1;
  128. build_tree(1);
  129. siji(j,1,20) {
  130. siji(i,1,n) {
  131. fa[i][j]=fa[fa[i][j-1]][j-1];
  132. val[i][j]=min(val[i][j-1],val[fa[i][j-1]][j-1]);
  133. }
  134. }
  135. scanf("%d",&Q);
  136. while(Q--) {
  137. scanf("%d",&m);
  138. int u;
  139. siji(i,1,m) {
  140. scanf("%d",&u);
  141. aux[i]=u;
  142. vis[u]=1;
  143. sol[i]=u;
  144. }
  145. aux[m+1]=1;
  146. build_auxtree(m+1);
  147. solve();
  148. siji(i,1,m) {
  149. vis[sol[i]]=0;
  150. }
  151. printf("%lld\n",dp[1]);
  152. }
  153. return 0;
  154. }

BZOJ 3110: [Zjoi2013]K大数查询

Description

有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c
如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。

Input

第一行N,M
接下来M行,每行形如1 a b c或2 a b c

Output

输出每个询问的结果

Sample Input

2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3

Sample Output

1
2
1

HINT

【样例说明】

第一个操作 后位置 1 的数只有 1 , 位置 2 的数也只有 1 。 第二个操作 后位置 1的数有 1 、 2 ,位置 2 的数也有 1 、 2 。 第三次询问 位置 1 到位置 1 第 2 大的数 是1 。 第四次询问 位置 1 到位置 1 第 1 大的数是 2 。 第五次询问 位置 1 到位置 2 第 3大的数是 1 。‍
N,M<=50000,N,M<=50000
a<=b<=N
1操作中abs(c)<=N
2操作中c<=Maxlongint

题解

整体二分
听说这个题的数据被加强了就变得特别的恶心
首先有一个注意点,它的绝对值小于N可能还会有负数,所以次数界应该是2×N+1
然后这个c是50000×50000会爆int……
读入没改成long long……一直wa……这真是个毒瘤题
我们把所有询问都读进来,对于取值范围进行二分,用树状数组维护一下每个区间里的和有多少,然后看看次数是否大于k,如果大于等于k那么说明它的第k大在左半区间,否则在右半区间

代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cstring>
  5. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  6. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  7. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  8. //#define ivorysi
  9. using namespace std;
  10. typedef long long ll;
  11. const int N=100500;
  12. int n,m,tot;
  13. int lowbit(int x) {return x&(-x);}
  14. struct BIT {
  15. ll f[N+5];
  16. BIT() {
  17. memset(f,0,sizeof(f));
  18. }
  19. void Insert(int pos,ll val) {
  20. while(pos<=tot) {
  21. f[pos]+=val;
  22. pos+=lowbit(pos);
  23. }
  24. }
  25. ll Query(int pos) {
  26. ll res=0;
  27. while(pos>0) {
  28. res+=f[pos];
  29. pos-=lowbit(pos);
  30. }
  31. return res;
  32. }
  33. void clear(int pos) {
  34. while(pos<=N) {
  35. f[pos]=0;
  36. pos+=lowbit(pos);
  37. }
  38. }
  39. }t,ts;
  40. void Insert(int l,int r,ll v) {
  41. t.Insert(l,v);
  42. t.Insert(r+1,-v);
  43. ts.Insert(l,(l-1)*v);
  44. ts.Insert(r+1,-r*v);
  45. }
  46. ll Query_prefix(int pos) {
  47. return t.Query(pos)*pos-ts.Query(pos);
  48. }
  49. ll Query(int l,int r) {
  50. return Query_prefix(r)-Query_prefix(l-1);
  51. }
  52. int op[N],a[N],b[N],cur[N],ans[N];
  53. ll c[N];
  54. int tmp1[N],tmp2[N];
  55. void Solve(int L,int R,int l,int r) {
  56. if(L>R) return;
  57. if(l==r) {
  58. siji(i,L,R) {
  59. if(op[cur[i]]==2) ans[cur[i]]=l;
  60. }
  61. return;
  62. }
  63. int idx1=0,idx2=0,mid=(l+r)>>1;
  64. siji(i,L,R) {
  65. if(op[cur[i]]==1) {
  66. if((int)c[cur[i]]<=mid) {
  67. Insert(a[cur[i]],b[cur[i]],1);
  68. tmp1[idx1++]=cur[i];
  69. }
  70. else tmp2[idx2++]=cur[i];
  71. }
  72. else {
  73. ll tmp=Query(a[cur[i]],b[cur[i]]);
  74. if(tmp<c[cur[i]]) {
  75. tmp2[idx2++]=cur[i];
  76. c[cur[i]]-=tmp;
  77. }
  78. else tmp1[idx1++]=cur[i];
  79. }
  80. }
  81. xiaosiji(i,0,idx1) {
  82. int k=tmp1[i];
  83. t.clear(a[k]);ts.clear(a[k]);
  84. t.clear(b[k]+1);ts.clear(b[k]+1);
  85. }
  86. int MID=L+idx1;
  87. xiaosiji(i,L,MID) cur[i]=tmp1[i-L];
  88. siji(i,MID,R) cur[i]=tmp2[i-MID];
  89. Solve(L,MID-1,l,mid);Solve(MID,R,mid+1,r);
  90. return;
  91. }
  92. int main() {
  93. #ifdef ivorysi
  94. freopen("f1.in","r",stdin);
  95. #endif
  96. scanf("%d%d",&n,&m);
  97. siji(i,1,m) {
  98. scanf("%d%d%d%lld",&op[i],&a[i],&b[i],&c[i]);
  99. if(op[i]==1) {c[i]=(ll)n-c[i]+1;tot=max(tot,(int)c[i]);}
  100. cur[i]=i;
  101. }
  102. Solve(1,m,1,2*n+1);
  103. siji(i,1,m) {
  104. if(op[i]==2) printf("%d\n",n-ans[i]+1);
  105. }
  106. }

2527: [Poi2011]Meteors

Description

Byteotian Interstellar Union (BIU) has recently discovered a new planet in a nearby galaxy. The planet is unsuitable for colonisation due to strange meteor showers, which on the other hand make it an exceptionally interesting object of study.
The member states of BIU have already placed space stations close to the planet's orbit. The stations' goal is to take samples of the rocks flying by. The BIU Commission has partitioned the orbit into msectors, numbered from 1to m, where the sectors 1and mare adjacent. In each sector there is a single space station, belonging to one of the nmember states.
Each state has declared a number of meteor samples it intends to gather before the mission ends. Your task is to determine, for each state, when it can stop taking samples, based on the meter shower predictions for the years to come.
Byteotian Interstellar Union有N个成员国。现在它发现了一颗新的星球,这颗星球的轨道被分为M份(第M份和第1份相邻),第i份上有第Ai个国家的太空站。
这个星球经常会下陨石雨。BIU已经预测了接下来K场陨石雨的情况。
BIU的第i个成员国希望能够收集Pi单位的陨石样本。你的任务是判断对于每个国家,它需要在第几次陨石雨之后,才能收集足够的陨石。
输入:
第一行是两个数N,M。
第二行有M个数,第i个数Oi表示第i段轨道上有第Oi个国家的太空站。
第三行有N个数,第i个数Pi表示第i个国家希望收集的陨石数量。
第四行有一个数K,表示BIU预测了接下来的K场陨石雨。
接下来K行,每行有三个数Li,Ri,Ai,表示第K场陨石雨的发生地点在从Li顺时针到Ri的区间中(如果Li<=Ri,就是Li,Li+1,...,Ri,否则就是Ri,Ri+1,...,m-1,m,1,...,Li),向区间中的每个太空站提供Ai单位的陨石样本。
输出:
N行。第i行的数Wi表示第i个国家在第Wi波陨石雨之后能够收集到足够的陨石样本。如果到第K波结束后仍然收集不到,输出NIE。
数据范围:
1<=n,m,k<=3*10^5
1<=Pi<=10^9
1<=Ai<10^9

Sample Input

3 5
1 3 2 1 3
10 5 7
3
4 2 4
1 3 1
3 5 2

Sample Output

3
NIE
1

题解

这道题整体二分的思路比较明显,然而在统计的过程中临时变量可能会爆long long解决方法就是中途不断判断如果>=就跳出
解题思路是二分一个结束点,然后看看每个国家都攒了多少陨石,把国家分到左右区间去

代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cmath>
  5. #include <cstring>
  6. #include <vector>
  7. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  8. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  9. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  10. #define pii pair<int,int>
  11. #define fi first
  12. #define se second
  13. #define INF 0x3f3f3f3f
  14. #define MAXN 300005
  15. //#define ivorysi
  16. using namespace std;
  17. typedef long long ll;
  18. struct node {
  19. int L,R,a;
  20. }qry[MAXN];
  21. int n,m,cur[MAXN],k,ans[MAXN],tmp1[MAXN],tmp2[MAXN],p[MAXN];
  22. vector<int> v[MAXN];
  23. ll tr[MAXN];
  24. int lowbit(int x) {return x&(-x);}
  25. void Insert(int pos,ll val) {
  26. while(pos<=m) {
  27. tr[pos]+=val;
  28. pos+=lowbit(pos);
  29. }
  30. }
  31. ll Query(int pos) {
  32. ll res=0;
  33. while(pos>0) {
  34. res+=tr[pos];
  35. pos-=lowbit(pos);
  36. }
  37. return res;
  38. }
  39. void solve(int L,int R,int l,int r) {
  40. if(L>R) return;
  41. if(l==r) {
  42. siji(i,L,R) {
  43. ans[cur[i]]=l;
  44. }
  45. return;
  46. }
  47. int mid=(l+r)>>1;
  48. siji(i,l,mid) {
  49. if(qry[i].L<=qry[i].R) {
  50. Insert(qry[i].L,qry[i].a);
  51. Insert(qry[i].R+1,-qry[i].a);
  52. }
  53. else {
  54. Insert(qry[i].L,qry[i].a);
  55. Insert(1,qry[i].a);
  56. Insert(qry[i].R+1,-qry[i].a);
  57. }
  58. }
  59. int idx1=0,idx2=0;
  60. siji(i,L,R) {
  61. int s=v[cur[i]].size();
  62. ll tmp=0;
  63. xiaosiji(j,0,s) {
  64. tmp+=Query(v[cur[i]][j]);
  65. if(tmp>=(ll)p[cur[i]]) break;
  66. }
  67. if(tmp>=(ll)p[cur[i]]) {
  68. tmp1[idx1++]=cur[i];
  69. }
  70. else {
  71. p[cur[i]]-=(int)tmp;
  72. tmp2[idx2++]=cur[i];
  73. }
  74. }
  75. siji(i,l,mid) {
  76. if(qry[i].L<=qry[i].R) {
  77. Insert(qry[i].L,-qry[i].a);
  78. Insert(qry[i].R+1,qry[i].a);
  79. }
  80. else {
  81. Insert(qry[i].L,-qry[i].a);
  82. Insert(1,-qry[i].a);
  83. Insert(qry[i].R+1,qry[i].a);
  84. }
  85. }
  86. int MID=L+idx1;
  87. xiaosiji(i,L,MID) cur[i]=tmp1[i-L];
  88. siji(i,MID,R) cur[i]=tmp2[i-MID];
  89. solve(L,MID-1,l,mid);solve(MID,R,mid+1,r);
  90. return ;
  91. }
  92. int main() {
  93. #ifdef ivorysi
  94. freopen("f1.in","r",stdin);
  95. #endif
  96. scanf("%d%d",&n,&m);
  97. int u;
  98. siji(i,1,m) {
  99. scanf("%d",&u);
  100. v[u].push_back(i);
  101. }
  102. siji(i,1,n) {
  103. cur[i]=i;
  104. scanf("%d",&p[i]);
  105. }
  106. scanf("%d",&k);
  107. siji(i,1,k) {
  108. scanf("%d%d%d",&qry[i].L,&qry[i].R,&qry[i].a);
  109. }
  110. qry[k+1].L=1,qry[k+1].R=m,qry[k+1].a=1000000000;
  111. solve(1,n,1,k+1);
  112. siji(i,1,n) {
  113. if(ans[i]==k+1) puts("NIE");
  114. else {
  115. printf("%d\n",ans[i]);
  116. }
  117. }
  118. return 0;
  119. }

BZOJ 2738: 矩阵乘法

Description

给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数。

Input

第一行两个数N,Q,表示矩阵大小和询问组数;
接下来N行N列一共N*N个数,表示这个矩阵;
再接下来Q行每行5个数描述一个询问:x1,y1,x2,y2,k表示找到以(x1,y1)为左上角、以(x2,y2)为右下角的子矩形中的第K小数。

Output

对于每组询问输出第K小的数。

Sample Input

2 2
2 1
3 4
1 2 1 2 1
1 1 2 2 3

Sample Output

1
3

HINT

矩阵中数字是109以内的非负整数;
20%的数据:N<=100,Q<=1000;
40%的数据:N<=300,Q<=10000;
60%的数据:N<=400,Q<=30000;
100%的数据:N<=500,Q<=60000。

题解

整体二分
这个需要离散化一下所有的值,减少二分次数
二分一个值,然后把小于等于这个数加入矩阵,用树状数组维护矩阵和
对于每一个询问看看矩阵和是否大于等于询问值,然后左右分别处理

代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cmath>
  5. #include <cstring>
  6. #include <vector>
  7. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  8. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  9. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  10. #define pii pair<int,int>
  11. #define fi first
  12. #define se second
  13. #define MAXN 500005
  14. //#define ivorysi
  15. using namespace std;
  16. typedef long long ll;
  17. int n,Q;
  18. int lowbit(int x) {return x&(-x);}
  19. struct BIT {
  20. ll f[505][505];
  21. void Insert(int x,int y,ll v) {
  22. while(x<=n) {
  23. int t=y;
  24. while(t<=n) {
  25. f[x][t]+=v;
  26. t+=lowbit(t);
  27. }
  28. x+=lowbit(x);
  29. }
  30. }
  31. ll Query(int x,int y) {
  32. ll res=0;
  33. while(x>0) {
  34. int t=y;
  35. while(t>0) {
  36. res+=f[x][t];
  37. t-=lowbit(t);
  38. }
  39. x-=lowbit(x);
  40. }
  41. return res;
  42. }
  43. }tl;
  44. void Insert_pos(int x,int y,int v) {
  45. tl.Insert(x,y,v);
  46. }
  47. ll Query_prefix(int x,int y) {
  48. return tl.Query(x,y);
  49. }
  50. ll Query(int x1,int y1,int x2,int y2) {
  51. return Query_prefix(x2,y2)-Query_prefix(x1-1,y2)-Query_prefix(x2,y1-1)+Query_prefix(x1-1,y1-1);
  52. }
  53. struct node {
  54. int x,y,v;
  55. bool operator < (const node &rhs) const {
  56. return v<rhs.v;
  57. }
  58. }point[MAXN];
  59. struct data {
  60. int x1,y1,x2,y2,k;
  61. }qry[MAXN];
  62. int cur[MAXN],ans[MAXN],tmp1[MAXN],tmp2[MAXN],num[MAXN],tot;
  63. void Solve(int L,int R,int l,int r,int st) {
  64. if(st>n*n) return;
  65. if(L>R) return;
  66. if(l==r) {
  67. siji(i,L,R) {
  68. ans[cur[i]]=num[l];
  69. }
  70. return;
  71. }
  72. int mid=(l+r)>>1,rd=n*n+1;
  73. for(int i=st;i<=n*n;++i) {
  74. if(point[i].v<=num[mid]) {
  75. Insert_pos(point[i].x,point[i].y,1);
  76. }
  77. else {
  78. rd=i;
  79. break;
  80. }
  81. }
  82. int idx1=0,idx2=0;
  83. siji(i,L,R) {
  84. ll tmp=Query(qry[cur[i]].x1,qry[cur[i]].y1,qry[cur[i]].x2,qry[cur[i]].y2);
  85. if(tmp>=(ll)qry[cur[i]].k) {
  86. tmp1[idx1++]=cur[i];
  87. }
  88. else {
  89. qry[cur[i]].k-=(int)tmp;
  90. tmp2[idx2++]=cur[i];
  91. }
  92. }
  93. xiaosiji(i,st,rd) {
  94. Insert_pos(point[i].x,point[i].y,-1);
  95. }
  96. int MID=L+idx1;
  97. xiaosiji(i,L,MID) cur[i]=tmp1[i-L];
  98. siji(i,MID,R) cur[i]=tmp2[i-MID];
  99. Solve(L,MID-1,l,mid,st);Solve(MID,R,mid+1,r,rd);
  100. }
  101. int main() {
  102. #ifdef ivorysi
  103. freopen("f1.in","r",stdin);
  104. #endif
  105. scanf("%d%d",&n,&Q);
  106. int u;
  107. siji(i,1,n) {
  108. siji(j,1,n) {
  109. int k=(i-1)*n+j;
  110. point[k].x=i;
  111. point[k].y=j;
  112. scanf("%d",&point[k].v);
  113. num[++tot]=point[k].v;
  114. }
  115. }
  116. siji(i,1,Q) {
  117. scanf("%d%d%d%d%d",&qry[i].x1,&qry[i].y1,&qry[i].x2,&qry[i].y2,&qry[i].k);
  118. cur[i]=i;
  119. }
  120. sort(point+1,point+n*n+1);
  121. sort(num+1,num+tot+1);
  122. tot=unique(num+1,num+tot+1)-num-1;
  123. Solve(1,Q,1,tot,1);
  124. siji(i,1,Q) {
  125. printf("%d\n",ans[i]);
  126. }
  127. return 0;
  128. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注