停课集训 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\n",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\n",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的常数,被搞疯了。。

  听说这周复习网络流?

转载于:https://www.cnblogs.com/wsy01/p/7912547.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值