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 }
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 }
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 }
这几道题是非常的漂亮的,像第一二题的转化真的是想不到啊= =第三题也得有很好的直觉在能做= =