牛客CSP-S提高组赛前集训营3

A 货物收集

显然是一个二分答案的题。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<queue>
 7 #define dbg(x) cerr << #x << " = " << x <<endl
 8 #define dbg2(x,y) cerr<< #x <<" = "<< x <<"  "<< #y <<" = "<< y <<endl
 9 using namespace std;
10 typedef long long ll;
11 typedef double db;
12 typedef pair<int,int> pii;
13 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
14 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
15 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;}
16 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;}
17 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
18 template<typename T>inline T read(T&x){
19     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
20     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
21 }
22 const int N=1e6+7,INF=0x3f3f3f3f;
23 int val[N];
24 int n,L,R,w;
25 struct thxorz{
26     int head[N],nxt[N<<1],to[N<<1],w[N<<1],tot;
27     inline void add(int x,int y,int z){
28         to[++tot]=y,nxt[tot]=head[x],head[x]=tot,w[tot]=z;
29         to[++tot]=x,nxt[tot]=head[y],head[y]=tot,w[tot]=z;
30     }
31 }G;
32 #define y G.to[j]
33 inline int dfs(int mid,int x,int fa){
34     int ret=val[x];
35     for(register int j=G.head[x];j;j=G.nxt[j])if(y^fa&&G.w[j]<=mid)ret+=dfs(mid,y,x);
36     return ret;
37 }
38 #undef y
39 int main(){//freopen("test.in","r",stdin);freopen("test.out","w",stdout);
40     read(n),read(w);L=INF;
41     for(register int i=2;i<=n;++i)read(val[i]);
42     for(register int i=1,x,y,z;i<n;++i)read(x),read(y),read(z),G.add(x,y,z),MIN(L,z),MAX(R,z);
43     int mid;
44     while(L<R){
45         mid=L+R>>1;
46         if(dfs(mid,1,0)>=w)R=mid;
47         else L=mid+1;
48     }
49     printf("%d\n",L);
50     return 0;
51 }
View Code

B 货物分组

写出一个裸的DP方程。$O(n^3)$.

$f_{i,k}=\min\{f_{j,k-1}+k(sum_i-sum_{j-1})+\max_{j+1\sim i}-\min_{j+1\sim i}\}$

然后因为这个涉及和$k$相关的乘积项,为了简化$k$这一维的状态,采用费用预算的思想,将后面所有组都提前加上一次他们总和。

所以去掉$k$这一维,$O(n^2)$。

$f_i=\min\{f_j-sum_j+\max_{j+1\sim i}-\min_{j+1\sim i}\}$

然后要考虑优化。我DP优化学傻掉了。`````

发现max和min实际上每次都是一个元素控制他前面的一段区间。这个可以用单调栈求出,然后max若干段区间和min若干段区间,每段最值都相同。那么,把方程前两项压入线段树,然后后面的max和min通过修改来完成:每次更新区间的时候,把弹栈的若干区间里的状态去掉他们原来的最值,加上新加入栈的最值。这个就是个区间修改,然后线段树维护最小值,每次转移区间查询即可。$O(n\log n)$。

这里用了标记永久化,实际上是学hkk的,可以发现常数小一些,核心在于在某节点内部的区间上传信息时都要加上自己打的标记。

WA*1:线段树里下标表示对应的状态前两项,单调栈里是最值。。搞反了导致line54~line57出错。。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<queue>
 7 #define mst(x) memset(x,0,sizeof x)
 8 #define dbg(x) cerr << #x << " = " << x <<endl
 9 #define dbg2(x,y) cerr<< #x <<" = "<< x <<"  "<< #y <<" = "<< y <<endl
10 using namespace std;
11 typedef long long ll;
12 typedef double db;
13 typedef pair<int,int> pii;
14 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
15 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
16 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;}
17 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;}
18 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
19 template<typename T>inline T read(T&x){
20     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
21     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
22 }
23 const int N=1e5+7;
24 const ll INF=1ll<<50;
25 struct segment_tree{
26     ll minv[N<<2],atag[N<<2];
27     #define lc i<<1
28     #define rc i<<1|1
29 //    segment_tree(){memset(minv,0x3f,sizeof minv);}
30     void update_add(int i,int L,int R,int ql,int qr,ll val){
31         if(ql<=L&&qr>=R)return minv[i]+=val,atag[i]+=val,void();
32         int mid=L+R>>1;
33         if(ql<=mid)update_add(lc,L,mid,ql,qr,val);
34         if(qr>mid)update_add(rc,mid+1,R,ql,qr,val);
35         minv[i]=_min(minv[lc],minv[rc])+atag[i];
36     }
37     ll query_min(int i,int L,int R,int ql,int qr){
38         if(ql<=L&&qr>=R)return minv[i];
39         int mid=L+R>>1;ll ret=INF;
40         if(ql<=mid)MIN(ret,query_min(lc,L,mid,ql,qr));
41         if(qr>mid)MIN(ret,query_min(rc,mid+1,R,ql,qr));
42         return ret+atag[i];
43     }//pay attention to the permanent tag...
44 }T;
45 int stkmin[N],stkmax[N],A[N],top1,top2;
46 ll s[N],f[N];
47 int n,w,lb;
48 
49 int main(){//freopen("2.in","r",stdin);//freopen("test.ans","w",stdout);
50     read(n),read(w);stkmin[0]=stkmax[0]=-1;
51     for(register int i=1;i<=n;++i)s[i]=s[i-1]+read(A[i]);
52     for(register int i=1;i<=n;++i){//dbg(i);
53         T.update_add(1,0,n-1,i-1,i-1,f[i-1]+s[n]-s[i-1]);
54         while(top1&&A[stkmin[top1]]>=A[i])T.update_add(1,0,n-1,stkmin[top1-1],stkmin[top1]-1,A[stkmin[top1]]),--top1;
55         T.update_add(1,0,n-1,stkmin[top1],i-1,-A[i]),stkmin[++top1]=i;//dbg(top1);
56         while(top2&&A[stkmax[top2]]<=A[i])T.update_add(1,0,n-1,stkmax[top2-1],stkmax[top2]-1,-A[stkmax[top2]]),--top2;
57         T.update_add(1,0,n-1,stkmax[top2],i-1,A[i]),stkmax[++top2]=i;//dbg(top2);
58         while(s[i]-s[lb]>w)++lb;
59         f[i]=T.query_min(1,0,n-1,lb,i-1);
60     }
61     printf("%lld\n",f[n]);
62     return 0;
63 }
View Code

C 地形计算

复杂度计算非常神仙的一个题。其实这题复杂度我也不会算,以下为口胡

顺便学习了一下三元环和四元环的计数。连接1(有复杂度证明)连接2(好像做法不对?)连接3

三元环的话主要就是因为定了向,好处有二:一是每个环只会被计一次,二是复杂度可以利用这个度数大小关系证出来是根号的。

四元环的话,类似。原来的三元环是度数大的向度数小的走,现在为了走四元环保证复杂度,只能每次走深度为2的路(相当于就走了一次三元环),然后所有终点相同、深度为二的里做统计计算。

不过并不是边直接从度数大的走向小的,这样会有四元环被漏计,为了保证每个环被算一次且仅一次,改变走边方法,枚举每一个起点$x$,然后枚举走到的$y$,要求度数$x>y$(相等时按编号),然后再从$y$开始遍历走到的$z$,要求度数也有$z<x$,这样,每个环只会被度数最大的那个点统计到。同时还可以保证复杂度:假设现在$y$度数小于$\sqrt{M}$,则$x$遍历图中每条边,到达的点向外拓展,复杂度$O(M\sqrt{M})$。若$y$度数大于$\sqrt{M}$,则利用度数传递性,$x$度数也应当大于根号,所以得知总的这样的$x$不会超过根号个,所以每一个$y$有不超过根号个入点$x$进来,对于每一个点$y$贡献复杂度$O(\sqrt{M}outdegree_y)$,又因为$\sum outdegree_y\le m$,所以总体也是$O(M\sqrt{M})$的。证毕。其实这也是三元环复杂度的证明方法。

然后这题就算权值就好了。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<queue>
 7 #define mst(x) memset(x,0,sizeof x)
 8 #define dbg(x) cerr << #x << " = " << x <<endl
 9 #define dbg2(x,y) cerr<< #x <<" = "<< x <<"  "<< #y <<" = "<< y <<endl
10 using namespace std;
11 typedef long long ll;
12 typedef double db;
13 typedef pair<int,int> pii;
14 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
15 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
16 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;}
17 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;}
18 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
19 template<typename T>inline T read(T&x){
20     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
21     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
22 }
23 const int N=1e5+7,P=1e9+7;
24 struct thxorz{
25     int head[N],nxt[N<<1],to[N<<1],tot;
26     inline void add(int x,int y){
27         to[++tot]=y,nxt[tot]=head[x],head[x]=tot;
28         to[++tot]=x,nxt[tot]=head[y],head[y]=tot;
29     }
30 }G;
31 int val[N],deg[N],vis[N],sum[N],cnt[N];
32 int n,m,ans;
33 inline void add(int&a,int b){((a+=b)>=P)&&(a-=P);}
34 inline int mod(int a){return a>=P?a-P:a;}
35 
36 int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout);
37     read(n),read(m);
38     for(register int i=1;i<=n;++i)read(val[i]);
39     for(register int i=1,x,y;i<=m;++i)read(x),read(y),G.add(x,y),++deg[x],++deg[y];
40     #define y G.to[i]
41     #define z G.to[j]
42     for(register int x=1;x<=n;++x){
43         for(register int i=G.head[x];i;i=G.nxt[i])if(deg[y]<deg[x]||deg[x]==deg[y]&&y<x){
44             for(register int j=G.head[y];j;j=G.nxt[j])if(deg[z]<deg[x]||deg[z]==deg[x]&&z<x){
45                 if(vis[z]^x)vis[z]=x,cnt[z]=1,sum[z]=val[y];
46                 else add(ans,mod(cnt[z]*1ll*(val[x]+val[z]+0ll+val[y])%P+sum[z])),++cnt[z],add(sum[z],val[y]);
47             }
48         }
49     }
50     printf("%d\n",ans);
51     return 0;
52 }
View Code

 

posted @ 2019-11-05 16:48  Ametsuji_akiya  阅读(271)  评论(0编辑  收藏  举报