T1 礼物
一道既不是很水其实又很水的题,n<=20,显然的状压。
定义f[i]为在i状态下的期望购买次数(0表示没买,1表示已经买了),题出的很毒瘤,0<pi<=1,所以不存在有的礼物买不到的情况,又简单了(可是我还是没有想到)。
然后状态转移其实很好想理解,i状态可能是从i'转移而来,也可能是从它本身转移过来,这就是买与不买的问题。
f[i]=Σ(f[i']*p[j])+f[i]*(1-Σp[j]) (注意反推,从结果开始向后推,这样才能使Σp[j]有意义,正推不好求买到重复和不买的情况)
解释也很简单:每个物品j有p[j]的概率选中,转移到f[i]的总期望次数显然是其加和的结果,还可以从本身转移,概率就是其他物品都不选的情况了。
注意一下和的处理,这道题差不多就结束了(skyh神仙15minAC)。
小弟不才。
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #define HZOI using namespace std 5 HZOI; 6 double p[25]; 7 long long w[25]; 8 int n; 9 double f[(1<<22)]; 10 int main() 11 { 12 long long ans=0; 13 scanf("%d",&n); 14 for (int i=1; i<=n; ++i) 15 { 16 scanf("%lf%lld",&p[i],&w[i]); 17 ans+=w[i]; 18 } 19 for (int i=1; i<(1<<n); ++i) 20 { 21 double P=0,F=0; 22 for (int j=i; j; j-=j&(-j)) 23 { 24 int k=j&(-j); 25 P+=p[(int)log2(k)+1]; 26 F+=f[i^k]*p[(int)log2(k)+1]; 27 } 28 f[i]=(F+1)/P; 29 } 30 printf("%lld %.3lf ",ans,f[(1<<n)-1]); 31 return 0; 32 }
T2 通讯
板子题,只能这么定义它了。
10%很好拿,直接边权相加,即为最终答案。
正解Tarjan缩点,然后……枚举每一条边,找入边边权最小的一条作为树边,问题就这么被很好的解决了。
题目有个坑就是很容易让人想到最小生成树(Kruskal算法),然后光荣的10分,这正是坑人的地方(还好我不会打最小生成树哈哈哈~),这种算法只适用于无向边的遍历找最小,如果是有向边,很可能生成的就不是一棵树了(也就是说从根节点不能到达所有的子节点),这就是算法的问题,但是同样是最小生成树的Prim算法应该可以实现有向图的遍历,目前还没有实现出来。
最后,注意,多测要清空。
小弟不才。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<queue> 6 #define HZOI using namespace std 7 HZOI; 8 int n,m; 9 int num,tt,vv[300003],first[300003],nx[300003],ww[300003]; 10 int tail,tot,dfn[100003],low[100003],be[100003],stack[100003],dis[100003]; 11 long long ans; 12 bool vis[100003]; 13 inline void Add(int ,int ,int ); 14 void Tarjan(int ); 15 inline void Clear(); 16 void Work(); 17 int main() 18 { 19 while (scanf("%d%d",&n,&m)==2) 20 { 21 if (!n and !m) break; 22 Clear(); 23 for (int i=1; i<=m; ++i) 24 { 25 int u,v,w; 26 scanf("%d%d%d",&u,&v,&w); 27 Add(u,v,w); 28 } 29 Tarjan(0); 30 for (int i=0; i<n; ++i) vis[i]=0; 31 memset(dis,0x3f3f3f3f,sizeof(dis)); 32 dis[0]=0; 33 for (int i=0; i<n; ++i) 34 if (!vis[be[i]]) 35 { 36 for (int j=first[be[i]]; j; j=nx[j]) 37 if (be[vv[j]]!=be[i]) 38 {dis[be[vv[j]]]=min(dis[be[vv[j]]],ww[j]);} 39 vis[be[i]]=1; 40 } 41 for (int i=0; i<n; ++i) vis[i]=0; 42 for (int i=0; i<n; ++i) 43 if (!vis[be[i]]) 44 {ans+=dis[be[i]];vis[be[i]]=1;} 45 printf("%lld ",ans); 46 } 47 return 0; 48 } 49 void Tarjan(int k) 50 { 51 dfn[k]=low[k]=++tot; 52 stack[++tail]=k; 53 vis[k]=1; 54 for (int i=first[k]; i; i=nx[i]) 55 { 56 int ver=vv[i]; 57 if (!dfn[ver]) 58 { 59 Tarjan(ver); 60 low[k]=min(low[k],low[ver]); 61 } 62 if (vis[ver]) low[k]=min(low[k],dfn[ver]); 63 } 64 if (dfn[k]==low[k]) 65 { 66 int to; 67 ++num; 68 do 69 { 70 to=stack[tail--]; 71 be[to]=k; 72 vis[to]=0; 73 for (int i=first[to]; i; i=nx[i]) 74 Add(be[to],vv[i],ww[i]); 75 } while (to!=k); 76 } 77 } 78 inline void Add(int u,int v,int w) 79 { 80 vv[++tt]=v; ww[tt]=w; nx[tt]=first[u]; first[u]=tt; 81 } 82 inline void Clear() 83 { 84 memset(vv,0,sizeof(vv)); 85 memset(first,0,sizeof(first)); 86 memset(nx,0,sizeof(nx)); 87 memset(ww,0,sizeof(ww)); 88 memset(vis,0,sizeof(vis)); 89 memset(dis,0,sizeof(dis)); 90 memset(dfn,0,sizeof(dfn)); 91 memset(low,0,sizeof(low)); 92 memset(be,0,sizeof(be)); 93 memset(stack,0,sizeof(stack)); 94 tail=tot=num=tt=ans=0; 95 }
T3 奇袭
神仙题。
考试暴力n4怒拿27。
正解分治+单调栈优化。
首先第一步,理解题意,接着转化题意。
因为每个点都在不同行的不同列,所以我们可以转化成:给定N个数的一个排列,问这个序列中有多少个子区间的数恰好是连续的。
然后n2算法就出来了:枚举每一个区间大小size,再枚举每一个起点i,记录最大值最小值,若max-min=size-1,则是合法的,67分。
据机房巨佬们说,n2算法加上一些神奇的剪枝,可以卡到91分,这性价比已经很高了。
天知道这能优化到nlogn。
我们分治每个区间,为[l~mid],[mid+1~r],然后就可以分情况讨论:
1.最大值最小值在同侧。
2.最大值最小值在异侧。
先从中间开始向两边扩展,计算每个点到中间的最值。
同侧情况比较好处理,枚举每一个左边界位置,通过max-min=r-l可以判断右边界位置,如果对应位置的条件合法(即max和min均在同侧),则ans++。
然后就是不同侧处理。
记住不能用memset,150000的数组足够把一个AC代码卡成64了。
小弟不才。
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #define HZOI using namespace std 5 HZOI; 6 const int N=50003; 7 int a[50003],stack[50003]; 8 int n,cnt,res; 9 int lmin[50003],lmax[50003],rmin[50003],rmax[50003],t[3*50003]; 10 int Work(int ,int ); 11 int Go_on(int ,int ,int ); 12 inline int read(); 13 int main() 14 { 15 n=read(); 16 for (int i=1; i<=n; ++i) 17 {int x; x=read(); a[x]=read();} 18 res=Work(1,n); 19 printf("%d ",res); 20 } 21 int Work(int l,int r) 22 { 23 if (l==r) return 1; 24 int mid=(l+r)>>1; 25 int ans=Work(l,mid)+Work(mid+1,r); 26 ans+=Go_on(l,r,mid); 27 return ans; 28 } 29 int Go_on(int l,int r,int mid) 30 { 31 int ans=0; 32 lmin[mid]=lmax[mid]=a[mid]; 33 rmin[mid+1]=rmax[mid+1]=a[mid+1]; 34 for (int i=mid-1; i>=l; --i) 35 {lmin[i]=min(lmin[i+1],a[i]); lmax[i]=max(lmax[i+1],a[i]);} 36 for (int i=mid+2; i<=r; ++i) 37 {rmin[i]=min(rmin[i-1],a[i]); rmax[i]=max(rmax[i-1],a[i]);} 38 for (int i=mid; i>=l; --i) 39 {int to=i+lmax[i]-lmin[i]; if (to>mid and to<=r and rmin[to]>lmin[i] and rmax[to]<lmax[i]) ++ans;} 40 for (int i=mid+1; i<=r; ++i) 41 {int to=i-rmax[i]+rmin[i]; if (to<=mid and to>=l and lmin[to]>rmin[i] and lmax[to]<rmax[i]) ++ans;} 42 int p=mid,q=mid; 43 while (lmin[p]>rmin[r] and p>=l) {t[lmax[p]+p+N]++; --p;} //left max 44 while (lmax[q]<rmax[r] and q>=l) {t[lmax[q]+q+N]--; --q;} //right min 45 for (int i=r; i>mid; --i) 46 { 47 while (lmin[p+1]<rmin[i] and p<mid) {++p; t[lmax[p]+p+N]--;} 48 while (lmax[q+1]>rmax[i] and q<mid) {++q; t[lmax[q]+q+N]++;} 49 if (t[rmin[i]+i+N]>0) {ans+=t[rmin[i]+i+N];} 50 } 51 for (int i=l; i<=mid; ++i) t[lmax[i]+i+N]=0; 52 p=mid+1; q=mid+1; 53 while (rmax[q]<lmax[l] and q<=r) {t[rmax[q]-q+N]--; ++q;} //right max 54 while (rmin[p]>lmin[l] and p<=r) {t[rmax[p]-p+N]++; ++p;} //left min 55 for (int i=l; i<=mid; ++i) 56 { 57 while (rmin[p-1]<lmin[i] and p>mid+1) {--p; t[rmax[p]-p+N]--;} 58 while (rmax[q-1]>lmax[i] and q>mid+1) {--q; t[rmax[q]-q+N]++;} 59 if (t[lmin[i]-i+N]>0) {ans+=t[lmin[i]-i+N];} 60 } 61 for (int i=mid+1; i<=r; ++i) t[rmax[i]-i+N]=0; 62 return ans; 63 } 64 inline int read() 65 { 66 int num=0; char ch=getchar(); 67 while (ch<'0' or ch>'9') ch=getchar(); 68 while (ch>='0' and ch<='9') num=(num<<3)+(num<<1)+(ch-48),ch=getchar(); 69 return num; 70 }
永不放弃。