HNOI2015 Day 1

HNOI2015的题还是非常漂亮的,几道题都有很大的借鉴意义,都有很强的思考性

T1亚瑟王(概率论)

描述:http://www.lydsy.com/JudgeOnline/problem.php?id=4008

我们可以发现这个模型可以转化成有r个机会给n个人,每个人对每个机会都有一定的概率拿,求收益的期望

那么记f[i][j]为第i个人有j个机会的概率,那么可以得出

f[i+1][j]+=f[i][j]*(1-p[i])^j

f[i+1][j-1]+=f[i][j]*(1-(1-p[i])^j)

就可以完美解决这个问题了

这道题的重点是把这个模型进行转化(可以直观了理解吧n和r对换过来),否则按 f[i][j]为第i轮到第j个的概率的话搞到死都做不出(我就是这样QAQ),在做概率题的时候模型转换是第一步也是最重要的一步

Code:

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cstring>
 5 using namespace std;
 6 #define maxn 222
 7 double f[maxn][maxn],p[maxn];
 8 int d[maxn];
 9 int main(){
10     int t;
11     scanf("%d",&t);
12     while (t--) {
13         memset(f,0,sizeof(f));
14         int n,r;
15         scanf("%d%d",&n,&r);
16         for (int i=1;i<=n;i++) scanf("%lf%d",p+i,d+i);
17         memset(f,0,sizeof(f));
18         f[1][r]=1;
19         double ans=0;
20         for (int i=1;i<=n;i++) {
21             double tmp=1;
22             for (int j=1;j<=r;j++){
23                 tmp*=1-p[i];
24                 f[i+1][j]+=f[i][j]*tmp;
25                 f[i+1][j-1]+=f[i][j]*(1-tmp);
26                 ans+=f[i][j]*(1-tmp)*d[i];
27             }
28         }
29         printf("%.10lf
",ans);
30     }
31     return 0;
32 }
View Code

T2:接水果(数据结构)

描述:http://www.lydsy.com/JudgeOnline/problem.php?id=4009

ORZ clj出的神题

由于省特派员送温暖,各种写法都水过了QAQ

连暴力在八中都能过这是什么回事QAQ

先讲下水法把= =

解法一:(暴力)

直接读入每个盘子后排下序,用lca判断覆盖就离线tarjan搞,复杂度O(n^2),水过了!!!

解法二:(树链剖分+主席树+可持久化treap)

先把那棵树树链剖分掉,然后考虑在没有主席树的远古时代人们用的方法——平衡树,那么我们对每一个树节点建立一棵主席树,每个主席树树节点维护一个可持久化Treap,存起点在该点在该树节点的重链上,终点在主席树的该区间中所有盘子的w值,那么我们每次就可以二分答案,把log n棵主席树,每棵主席树中的log n个区间中小于该答案的盘子数算出来= =总的时间复杂度O(n log^4 n)~~~

解法三:(点剖+树链剖分+主席树套权值线段树)

由于某同学的口胡,我至今仍搞不清为何要先点剖= =,大致思想跟解法二差不多,但是就吧treap换成权值线段树,总时间复杂度貌似为(O(n log^3 n)(某同学说的))

解法四:(树上莫队+平衡树)

既然题目允许离线那就考虑用树上莫队解决,对当前的两个询问点u,v维护他们的相对差(也就是u到根的信息-v到根的信息),lca上的信息需要特判一下,如果我的盘子的起点和终点都在我的相对差中就加入平衡树,然后在log n查询即可,分块上每根号n个一块即可通过本题,总的时间复杂度O(nsqrt(n)log n)

解法五:(正解,扫描线+树状数组套权值线段树)

还是离线,把每个点重标号为其dfs序,每个询问抽象成二维平面坐标中的点(u,v)。可发现每个盘子所控制的坐标为若干个矩形区域。

具体来说,如果两个盘子的坐标与其lca不同,那么就是起点与终点在u,v的子树中是符合要求的

否则,就是一个在子树中,另一个不在lca的那个子树中

那么问题就变成了一个经典问题:询问在某点上的矩形的第k大

扫描线+树状数组套权值线段树秒过

时间复杂度O(n log^2 n)

Code:

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<algorithm>
  4 #include<cstring>
  5 #include<vector>
  6 using namespace std;
  7 #define maxn 41000
  8 #define maxk 24
  9 vector<int> e[maxn];
 10 int st[maxn],ed[maxn],dfn[maxn],dep[maxn];
 11 int f[maxn][maxk],clo;
 12 void dfs(int u,int fa) {
 13     st[u]=ed[u]=dfn[u]=++clo;
 14     f[u][0]=fa;dep[u]=dep[fa]+1;
 15     for (int i=1;i<maxk;i++) {
 16         if (f[f[u][i-1]][i-1]==0) break;
 17         f[u][i]=f[f[u][i-1]][i-1];
 18     }
 19     for (int i=0;i<e[u].size();i++) {
 20         if (e[u][i]==fa) continue;
 21         dfs(e[u][i],u);
 22         ed[u]=ed[e[u][i]];
 23     }
 24 }
 25 inline int up(int x,int y) {
 26     for (int i=0;i<maxk;i++) if (y&(1<<i)) x=f[x][i];
 27     return x;
 28 }
 29 inline int lca(int x,int y) {
 30     if (dep[x]<dep[y]) swap(x,y);
 31     x=up(x,dep[x]-dep[y]);
 32     if (x==y) return x;
 33     for (int i=maxk-1;i+1;i--) {
 34         if (f[x][i]==f[y][i]) continue;
 35         x=f[x][i];y=f[y][i];
 36     }
 37     return f[x][0];
 38 }
 39 struct qnode{int x,y1,y2,z,flag;}s[maxn*10];
 40 int l;
 41 inline void add(int x,int y1,int y2,int w,int flag) {
 42     s[++l]=(qnode){x,y1,y2,w,flag};
 43 }
 44 typedef pair<int,int> ii;
 45 #define fi first
 46 #define se second
 47 ii b[maxn];
 48 bool cmp(int x,int y) {return dfn[b[x].fi]<dfn[b[y].fi];}
 49 bool cmp1(qnode x,qnode y){return x.x<y.x;}
 50 struct node{
 51     int lc,rc,s;
 52 }t[maxn*1000];
 53 int cnt;
 54 #define lc(x) t[x].lc
 55 #define rc(x) t[x].rc
 56 #define mid (l+r>>1)
 57 void insert(int &x,int l,int r,int y,int z){
 58     if (x==0) x=++cnt;
 59     t[x].s+=z;
 60     if (l==r) return ;
 61     if (mid>=y) insert(lc(x),l,mid,y,z);
 62     else insert(rc(x),mid+1,r,y,z);
 63 }
 64 int n;
 65 #define lowbit(x) ((x)&(-x))
 66 int r[maxn];
 67 #define inf 0x7fffffff
 68 inline void ins(int x,int y,int z) {
 69     for (;x<=n+3;x+=lowbit(x)) insert(r[x],0,inf,y,z);
 70 }
 71 vector<int> list[33];
 72 #define pb push_back
 73 int solve(int l,int r,int deep,int &k) {
 74     int sum=0;
 75     for (int i=0;i<list[deep].size();i++) sum+=t[list[deep][i]].s;
 76     if (sum<k) {k-=sum;return -1;}
 77     if (sum>=k&&l==r) return l;
 78     list[deep+1].clear();
 79     for (int i=0;i<list[deep].size();i++) if (t[list[deep][i]].lc) list[deep+1].pb(t[list[deep][i]].lc);
 80     int tmp=solve(l,mid,deep+1,k);
 81     if (tmp!=-1) return tmp;
 82     list[deep+1].clear();
 83     for (int i=0;i<list[deep].size();i++) if (t[list[deep][i]].rc) list[deep+1].pb(t[list[deep][i]].rc);
 84     return solve(mid+1,r,deep+1,k);
 85 }    
 86 inline int que(int x,int y,int k) {
 87     list[1].clear();
 88     for (;y;y-=lowbit(y)) if (r[y]) list[1].pb(r[y]);
 89     return solve(0,inf,1,k);
 90 }
 91 int main(){
 92     int p,q;
 93     scanf("%d%d%d",&n,&p,&q);
 94     for (int i=1;i<n;i++) {
 95         int x,y;
 96         scanf("%d%d",&x,&y);
 97         e[x].pb(y);e[y].pb(x);
 98     }
 99     dfs(1,0);
100     static ii a[maxn];
101     static int w[maxn],k[maxn];
102     for (int i=1;i<=p;i++) scanf("%d%d%d
",&a[i].fi,&a[i].se,w+i);
103     for (int i=1;i<=q;i++) scanf("%d%d%d
",&b[i].fi,&b[i].se,k+i);
104     for (int i=1;i<=p;i++) {
105         int v=lca(a[i].fi,a[i].se);
106         if (v!=a[i].fi&&v!=a[i].se) {
107             add(st[a[i].fi],st[a[i].se],ed[a[i].se],w[i],1);
108             add(ed[a[i].fi]+1,st[a[i].se],ed[a[i].se],w[i],-1);
109             add(st[a[i].se],st[a[i].fi],ed[a[i].fi],w[i],1);
110             add(ed[a[i].se]+1,st[a[i].fi],ed[a[i].fi],w[i],-1);
111         }else {
112             if (v!=a[i].fi) swap(a[i].fi,a[i].se);
113             v=up(a[i].se,dep[a[i].se]-dep[a[i].fi]-1);
114             add(1,st[a[i].se],ed[a[i].se],w[i],1);
115             add(st[v],st[a[i].se],ed[a[i].se],w[i],-1);
116             add(ed[v]+1,st[a[i].se],ed[a[i].se],w[i],1);
117             add(n+1,st[a[i].se],ed[a[i].se],w[i],-1);
118             add(st[a[i].se],1,st[v]-1,w[i],1);
119             add(ed[a[i].se]+1,1,st[v]-1,w[i],-1);
120             add(st[a[i].se],ed[v]+1,n,w[i],1);
121             add(ed[a[i].se]+1,ed[v]+1,n,w[i],-1);
122         }
123     }
124     static int id[maxn];
125     for (int i=1;i<=q;i++) id[i]=i;
126     sort(id+1,id+1+q,cmp);
127     sort(s+1,s+1+l,cmp1);
128     int r=1;
129     static int ans[maxn];
130     for (int i=1;i<=l;i++) {
131         while (r<=q&&dfn[b[id[r]].fi]<s[i].x) ans[id[r]]=que(b[id[r]].fi,dfn[b[id[r]].se],k[id[r]]),r++;
132         ins(s[i].y1,s[i].z,s[i].flag);
133         ins(s[i].y2+1,s[i].z,-s[i].flag);
134     }
135     for (int i=1;i<=q;i++) printf("%d
",ans[i]);
136     return 0;
137 }
View Code

T3:菜肴制作(拓扑序,贪心,构造)

描述:http://www.lydsy.com/JudgeOnline/problem.php?id=4010

首先判环这个就不用说了吧

然后我们必须优先让最小的尽量前,那么我们就必须且只能把最小的点反向边所能遍历到的点删掉

考虑删最小的点的前一个点,一定是去掉最小点反向边入度为0的最大点删去

可以继续贪心构造

那么我们每次就反向遍历求出联通块再做次拓扑序即可

优先队列 O(n log n)解决

Code:

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<queue>
 6 #include<vector>
 7 using namespace std;
 8 #define maxn 100010
 9 struct edges{
10     int to,next;
11 }edge[maxn];
12 int next[maxn],l,in[maxn];
13 inline void addedge(int x,int y) {
14     edge[++l]=(edges){y,next[x]},next[x]=l;in[y]++;
15 }
16 bool b[maxn];
17 vector<int> a;
18 #define pb push_back 
19 inline void tuopu(int x) {
20     static priority_queue<int> q;
21     while (!q.empty()) q.pop();
22     q.push(x);b[x]=1;a.clear();
23     while (!q.empty()) {
24         int u=q.top();q.pop();
25         a.pb(u);
26         for (int i=next[u];i;i=edge[i].next) {
27             if (b[edge[i].to]) continue;
28             in[edge[i].to]--;
29             if (in[edge[i].to]==0){
30                 b[edge[i].to]=1;
31                 q.push(edge[i].to);
32             }
33         }
34     }
35 }
36 int n;
37 bool iscircle() {
38     static int q[maxn];
39     int r=0;
40     for (int i=1;i<=n;i++) if (in[i]==0) q[++r]=i;
41     for (int l=1,u=q[l];l<=r;u=q[++l]) 
42         for (int i=next[u];i;i=edge[i].next){
43             in[edge[i].to]--;
44             if (in[edge[i].to]==0) q[++r]=edge[i].to;
45         }
46     return r!=n;
47 }
48 int cnt,bo[maxn];
49 bool bfs(int x) {
50     cnt++;
51     static int q[maxn];
52     q[1]=x;
53     for (int l=1,u=q[1],r=1;l<=r;u=q[++l]) 
54         for (int i=next[u];i;i=edge[i].next) {
55             if (b[edge[i].to]) continue;
56             in[edge[i].to]++;
57             if (bo[edge[i].to]!=cnt) {
58                 q[++r]=edge[i].to;
59                 bo[edge[i].to]=cnt;
60             }
61         }
62 }
63 int main(){
64     int T;
65     scanf("%d",&T);
66     while (T--) {
67         int m;
68         scanf("%d%d",&n,&m);
69         memset(next,0,sizeof(next));
70         memset(b,0,sizeof(b));
71         memset(bo,0,sizeof(bo));
72         memset(in,0,sizeof(in));
73         l=0;cnt=0;
74         for (int i=1;i<=m;i++) {
75             int x,y;
76             scanf("%d%d",&x,&y);
77             addedge(y,x);
78         }
79         if (iscircle()) {printf("Impossible!
");continue;}
80         for (int i=1;i<=n;i++) {
81             if (b[i]) continue;
82             bfs(i);
83             tuopu(i);
84             for (int i=a.size()-1;i+1;i--) printf("%d ",a[i]);
85         }
86         printf("
");
87     }
88     return 0;
89 }
View Code

 这几道题是非常的漂亮的,像第一二题的转化真的是想不到啊= =第三题也得有很好的直觉在能做= =

原文地址:https://www.cnblogs.com/New-Godess/p/4450078.html