训练日志4 (7.16)

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\n%.3lf\n",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\n",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\n",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 }
奇袭

 

  

 

  永不放弃。

posted @ 2019-07-17 14:02  _LH  阅读(171)  评论(0编辑  收藏  举报