zoukankan      html  css  js  c++  java
  • 停课集训 11.27

      改错之日  

      今天开始停课集训了,还有点茫然。

      今天首先改了一下noip的几个题目,就做一个总结吧。

      D1T1我找规律秒出a*b-a-b,开考1min打完,又写了个完全背包暴力对拍了一波发现正确。写完第二题之后简单证明了一下就不管了。。结果2min最后发现long long问题,天不亡我。

      D1T2模拟,开始搞啊搞,记了几个数组还有点麻烦,还开了个栈。出成绩之后发现炸了,看了代码发现初始化memset少了一个数组。药丸。

      D1T3废了我2h结果爆零了MD。dp定义都没想出来。最后时间不够慌忙打了个暴力还错了。。

      D2T1并查集判联通,10min打完。

      D2T2写了个状压dp,结果是伪的,有点错误。还好数据水给了我95,舒服。

      D2T3废了2.5h还是没想出来怎么线段树维护,倒是想到了动态开点233。

      

      正解的话。

      D1T3

      判断无解可以记录0环,然后对于0环里的每一个点i判断dis1[i]+dis2[i]<=dis1[n]+k就行。判断之后0环可以在图中去掉。

      用dis1[i]表示1到i的最短路,dis2[i]表示n到i的最短路。定义 f[i][j]表示走到i点 路径长=dis1[i]+j的方案数。无0边,把所有点按照dis1[]排序保证转移不错误。有0边,就对于0边连的两点,按拓扑序排序。总的来说就是先按dis1[]排序,再按拓扑序。

      转移先枚举j,再枚举i,排序后i能到达的点均在i后,不会漏算方案。

      具体的话,我改的时候 写的对于0边topsort求环并判断转移顺序。但常数巨大无比。此时发现一个优化,对于0环可以不转移,那么就可以重构图来进行转移了。改完之后卡过。

      然后看了同学的。记忆搜暴快,1.5s过所有点。算法大体还是一样的。用spfa判0环,标记所有在0环的点,记忆搜的时候只要有个在0环上的点搜到n点去了直接输出-1就好。

      gtmd为什么记忆搜这么强?我大概想了一下:递推的话所有状态是枚举完全的,而记忆搜可能出现很多废弃状态 ,再加一个剪枝,常数就很小了。

      总结:此题考场上想到定义的话无0边70分是很好拿的,但我就是蠢。。

      代码很丑,懒得挂出来丢人了。

      D2T2

      先说我的错误方法:f[st]表示状态为st的最小代价,并记录g[st][i]表示在st状态最小代价的情况下,树根到i的距离。转移取min{cost}并转移树。if(f[s']+cost<f[s])f[s]=f[s']+cost

      当时也是自信,打完就没管了。下来之后回想了一波发现并不那么正确,慌得一匹。

      不正确的话应该是树的形态,当前最优不一定以后最优。。

      我还有个奇思妙想:当f[s']+cost==f[s]的时候,比较两棵树的最大深度,最大深度小的期望cost更小,转移树的形态。诡异。

      

      实际上用f[i][st]表示最深一层的深度为i,状态为s。h[i][st]表示st中不含i,把i加进st的最小代价。g[s][s']表示s'是s的子集,用s'转移到s的最小边权和。

      f[i][s']转移到f[i+1][s] 。s'是s的子集,我们强行规定把所有的 属于s不属于s'的的节点加在树的最后一层

      即f[i+1][s]=min(f[i+1][s],f[i][s']+i*g[s][s'])

      那么为什么强行这么规定会正确?

      考虑:对于这种规定会有误差使f[i+1][s]偏大,而我们要求的答案一定会被枚举到且精准无误。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<algorithm>
     4 #include<cstring>
     5 #define ll long long
     6 #define inf 0x3f3f3f3f
     7 #define N 1<<12
     8 using namespace std;
     9 int f[13][N],g[N][N],h[13][N],d[13][13],n,m;
    10 int main(){
    11     freopen("treasure.in","r",stdin);
    12     freopen("treasure.out","w",stdout);
    13     scanf("%d%d",&n,&m);
    14     memset(d,0x3f,sizeof(d));
    15     memset(h,0x3f,sizeof(h));
    16     for(int i=1;i<=m;i++){
    17         int a,b,c;
    18         scanf("%d%d%d",&a,&b,&c);
    19         d[a][b]=d[b][a]=min(d[a][b],c);
    20     }
    21     int mst=(1<<n)-1;
    22     for(int i=1;i<=n;i++){
    23         for(int j=0;j<=mst;j++){
    24             if(1<<(i-1)&j)continue;
    25             for(int k=1;k<=n;k++){
    26                 if(k==i)continue;
    27                 if(1<<(k-1)&j)h[i][j]=min(h[i][j],d[i][k]);
    28             }
    29         }
    30     }
    31     for(int s=0;s<=mst;s++){
    32         int t=s&(s-1);
    33         while(t){
    34             int x=s^t;
    35             for(int i=1;i<=n;i++){
    36                 if(x&1<<(i-1))
    37                 g[s][t]+=h[i][t];
    38                 if(g[s][t]>inf)g[s][t]=inf;
    39             }
    40             t=s&(t-1);
    41         }
    42     }
    43     memset(f,0x3f,sizeof(f));
    44     for(int i=1;i<=n;i++)f[1][1<<(i-1)]=0;
    45     for(int i=2;i<=n;i++)
    46     for(int s=0;s<=mst;s++){
    47         int t=s&(s-1);
    48         while(t){
    49             int tmp;
    50             if(g[s][t]<inf)tmp=(i-1)*g[s][t];
    51             else tmp=inf;
    52             f[i][s]=min(f[i][s],f[i-1][t]+tmp);
    53             t=s&(t-1);
    54         }
    55     }
    56     int ans=inf;
    57     for(int i=1;i<=n;i++)ans=min(ans,f[i][mst]);
    58     printf("%d",ans);
    59     return 0;
    60 }
    View Code

      

      D2T3

      从未见过这么厚颜无耻的题,听出题人说还卡常。一开始以为是n*m<=3e5,md竟然是n<=3e5,m<=3e5

      noip第一次引入log数据结构竟然这么难,给未成年人的心理留下了难以磨灭的阴影。

      考虑只有一行的情况。我们可以建立一棵m+q大小权值线段树,最下层节点存放人的标号,一开始把1~m区间都sum设为区间长度。走一个就把那个位置sum变成0,向上传递。最后在屁股后面加上出队这个人。查询就可以查找哪个位置前面有k个1。

      (考试的时候我想到这里就能A掉他啊woc,还是道行太浅了了)

      推广到n*m

      n+1棵线段树,1~n表示每行1~m-1位置,n+1单独表示最后一列。

      那么出队一个人(x,y),分类讨论。y==m就在第n+1棵树里面找第x个1位置得人,y<m就在第x棵树里找第y个1位置的人。

      y==m的话,跟一行的情况类似,拖出来再甩到屁股后面。

      y<m,在第x棵树里把那个人拖出来扔到树n+1屁股后面。把n+1棵树的第y个1位置的人拖出来甩到树x屁股后面就ok了。

      具体的细节参加代码,主要是动态开点有点麻烦。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<algorithm>
     4 #include<cstring>
     5 #define ll long long
     6 #define N 300005
     7 using namespace std;
     8 int n,m,q,cnt,rt[N],ed[N];
     9 struct tree{int ls,rs,sum;ll x;}t[N*60];
    10 ll ans;
    11 void pushup(int u){
    12     int l=t[u].ls,r=t[u].rs;
    13     t[u].sum=t[l].sum+t[r].sum;
    14 }
    15 
    16 void query(int u,int L,int R,int pos,int k){
    17     if(L==R){
    18         if(!t[u].x){
    19             if(pos==n+1)t[u].x=(ll)L*m;
    20             else t[u].x=1ll*(pos-1)*m+L;
    21         }
    22         ans=t[u].x;t[u].sum=0;
    23         return;
    24     }
    25     int mid=L+R>>1;
    26     if(!t[u].ls){
    27         t[u].ls=++cnt;
    28         t[t[u].ls].sum=mid-L+1;
    29     }
    30     if(!t[u].rs){
    31         t[u].rs=++cnt;
    32         t[t[u].rs].sum=R-mid;
    33     }
    34     if(t[t[u].ls].sum>=k)query(t[u].ls,L,mid,pos,k);
    35     else query(t[u].rs,mid+1,R,pos,k-t[t[u].ls].sum);
    36     pushup(u);
    37 }
    38 
    39 void update(int u,int L,int R,int l,int r,int op,ll val){
    40     if(l<=L&&R<=r){
    41         if(!op)t[u].sum=R-L+1;
    42         else t[u].sum=1,t[u].x=val;
    43         return;
    44     }
    45     int mid=L+R>>1;
    46     if(!t[u].ls)t[u].ls=++cnt;
    47     if(!t[u].rs)t[u].rs=++cnt;
    48     if(mid>=l)update(t[u].ls,L,mid,l,r,op,val);
    49     if(mid<r)update(t[u].rs,mid+1,R,l,r,op,val);
    50     pushup(u);
    51 }
    52 
    53 int main(){
    54     freopen("phalanx.in","r",stdin);
    55     freopen("phalanx.out","w",stdout);
    56     scanf("%d%d%d",&n,&m,&q);
    57     int len=m-1+q,lem=n+q;
    58     //rt[1~n]为1~n行前m-1个 rt[n+1]为第m列 
    59     for(int i=1;i<=n;i++){
    60         rt[i]=++cnt;ed[i]=m-1;//ed记录队尾 
    61         update(rt[i],1,len,1,m-1,0,0);
    62     }
    63     rt[n+1]=++cnt;ed[n+1]=n;
    64     update(rt[n+1],1,lem,1,n,0,0);
    65     for(int i=1;i<=q;i++){
    66         int x,y;
    67         scanf("%d%d",&x,&y);
    68         if(y==m){
    69             query(rt[n+1],1,lem,n+1,x);//抽出第m列第x个 
    70             printf("%I64d
    ",ans);ed[n+1]++;
    71             update(rt[n+1],1,lem,ed[n+1],ed[n+1],1,ans);//把抽出的那个放进m列尾 
    72         }
    73         else{
    74             query(rt[x],1,len,x,y);//抽出(x,y) 
    75             ed[n+1]++;update(rt[n+1],1,lem,ed[n+1],ed[n+1],1,ans);//把(x,y)放进(n,m+1) 
    76             printf("%I64d
    ",ans);
    77             query(rt[n+1],1,lem,n+1,x);//抽出(x,m) 
    78             ed[x]++;update(rt[x],1,len,ed[x],ed[x],1,ans);//把(x,m)放进(x,m-1)
    79         }
    80     }
    81     return 0;
    82 }
    View Code

      本来下午就改完了的,晚上一直优化D1T3的常数,被搞疯了。。

      听说这周复习网络流?

  • 相关阅读:
    jQuery下拉框操作系列$("option:selected",this) &&(锋利的jQuery)
    Jquery全选系列操作(锋利的jQuery)
    ASP.NET MVC 简单事务添加
    LINQ语法类似于SQL的语法
    C#(简单递归)和实现IComparable接口
    Jquery使用Id获取焦点和失去焦点
    解决使用C#打开第三方应用后进程关联问题
    使用 NSIS 制作安装包
    C# 窗口程序闪退
    C++ std::string 不可初始化为NULL
  • 原文地址:https://www.cnblogs.com/wsy01/p/7912547.html
Copyright © 2011-2022 走看看