商店在开业的m个小时中会接到一些订单,给出每个小时生产的代价,产品保存周期,和产品保存1小时的代价,求完成所有订单的最小消耗.
算出所有订单的时间戳,预处理出1<=t<=m小时做出来的月饼保存到m的代价cost[t],
假设现在需要知道订单 i 的最优代价,那么查找它的时间戳 time[i]与它之前t小时的代价中最小
的就可以了,需要用到rmq.
1 #include<stdio.h> 2 #include<string.h> 3 #include<math.h> 4 #include<iostream> 5 #define maxn 2555 6 #define maxm 220000 7 using namespace std; 8 typedef long long llong; 9 llong val[maxm],tval[maxm],num[maxn]; 10 int times[maxn],n,m,s,t,rmq[maxm][20]; 11 12 char Month[12][4]={"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov","Dec"}; 13 int Day[12]={31,28,31,30,31,30,31,31,30,31,30,31}; 14 int leap(int year) 15 { 16 if(year%100==0){ 17 if(year%400==0)return 1; 18 else return 0; 19 } 20 if(year%4==0)return 1; 21 else return 0; 22 } 23 int get_time(char* str,int day,int year,int h) 24 { 25 llong res=0; 26 int i,month=0; 27 for(i=0;i<12;i++) 28 if(memcmp(str,Month[i],sizeof(str))==0) 29 {month=i+1;break;} 30 31 for(i=2000;i<year;i++){ 32 if(leap(i))res+=366*24; 33 else res+=365*24; 34 } 35 for(i=1;i<month;i++){ 36 res+=24*Day[i-1]; 37 if(i==2 && leap(year))res+=24; 38 } 39 for(i=1;i<day;i++) 40 res+=24; 41 res+=h; 42 return res+1; 43 } 44 void input() 45 { 46 int i,year,day,h,r; 47 char str[4]; 48 for(i=1;i<=n;i++){ 49 scanf("%s%d%d%d%d",str,&day,&year,&h,&r); 50 times[i]=get_time(str,day,year,h); 51 num[i]=r; 52 } 53 scanf("%d%d",&t,&s); 54 for(i=1;i<=m;i++)cin>>val[i]; 55 } 56 void init_rmq() 57 { 58 int i,j; 59 for(i=1;i<=m;i++)rmq[i][0]=i; 60 61 for(j=1;(1<<j)<m;j++) 62 for(i=1;i+(1<<j)-1<=m;i++){ 63 if(tval[rmq[i][j-1]] < tval[rmq[i+(1<<(j-1))][j-1]]) 64 rmq[i][j]=rmq[i][j-1]; 65 else 66 rmq[i][j]=rmq[i+(1<<(j-1))][j-1]; 67 } 68 } 69 int get_rmq(int l,int r) 70 { 71 int rg,k; 72 if(l<1)l=1; 73 rg=r-l+1; 74 k=log((double)rg)/log(2.0); 75 if(tval[rmq[l][k]]>tval[rmq[r-(1<<k)+1][k]]) 76 return rmq[r-(1<<k)+1][k]; 77 else 78 return rmq[l][k]; 79 } 80 void init() 81 { 82 int i; 83 for(i=1;i<=m;i++) 84 tval[i]=val[i]+(m-i)*s; 85 init_rmq(); 86 } 87 void solv() 88 { 89 int i,find; 90 llong cnt=0,add; 91 for(i=1;i<=n;i++) 92 { 93 find=get_rmq(times[i]-t,times[i]); 94 add=val[find]+(times[i]-find)*s; 95 cnt+=num[i]*add; 96 } 97 cout<<cnt<<endl; 98 } 99 int main() 100 { 101 while(scanf("%d%d",&n,&m)) 102 { 103 if(n+m==0)break; 104 input(); 105 init(); 106 solv(); 107 } 108 return 0; 109 }
给出一颗带边权的树,先求从每个点出发的最长路径,然后处理m个询问:
最多取多少个标号连续的顶点,保证他们最长路径长的差值不超过q.
首先用树形dp预处理每个点的最长路径.
处理询问可以贪心:
由于必须取连续的点,当我们从标号为 l 的点开始一直取到标号为r的点, 发现r与中间的mid矛盾
不能取,我们要么是舍弃l~mid段,从mid+1开始取,要么是前面都舍弃,从r开始取,显然要选前者.
接下来就可以用rmq处理了,每加入一个点前,先用rmq找到此时区间中的最大和最小值,和新加的比较,判断是否有冲突.
此题还有一个trick:即使舍去l~mid部分的区间,r可能仍然不能加入,所以应该不断地缩小区间,判断,直至r可以取时为止,好在
rmq_st算法的查询复杂度是O(1)的,这样查询的复杂度是O(n),总复杂度是O(n*m).
1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 #define maxn 50005 5 using namespace std; 6 typedef struct edge{ 7 int v;int nxt;int d; 8 }edge;edge e[maxn*2]; 9 typedef struct nd{ 10 int id;int d; 11 }nd;nd len[2][maxn]; 12 int hd[maxn<<1],ne,l[maxn]; 13 int n,m,frt,ral,rbq[maxn][17],rsq[maxn][17]; 14 void add_edge(int u,int v,int d) 15 { 16 e[ne].v=v;e[ne].nxt=hd[u]; 17 e[ne].d=d;hd[u]=ne++; 18 } 19 void input() 20 { 21 int i,u,v,d; 22 memset(hd,-1,sizeof(hd)); 23 for(i=1,ne=0;i<n;i++){ 24 scanf("%d%d%d",&u,&v,&d); 25 add_edge(u,v,d); 26 add_edge(v,u,d); 27 } 28 } 29 void dfs1(int fa,int cur) 30 { 31 int i,v; 32 33 for(i=hd[cur];i!=-1;i=e[i].nxt) 34 if(e[i].v!=fa) 35 dfs1(cur,e[i].v); 36 37 for(i=hd[cur];i!=-1;i=e[i].nxt) 38 if(e[i].v!=fa){ 39 v=e[i].v; 40 if(len[0][cur].d<len[0][v].d+e[i].d){ 41 len[1][cur].d=len[0][cur].d,len[1][cur].id=len[0][cur].id; 42 len[0][cur].d=len[0][v].d+e[i].d;len[0][cur].id=e[i].v; 43 }else if(len[1][cur].d<len[0][v].d+e[i].d) 44 len[1][cur].d=len[0][v].d+e[i].d,len[1][cur].id=e[i].v; 45 } 46 } 47 void dfs2(int fa,int cur) 48 { 49 int i,v; 50 for(i=hd[cur];i!=-1;i=e[i].nxt) 51 if(e[i].v!=fa){ 52 v=e[i].v; 53 if(len[0][cur].id == v){ 54 if(len[0][v].d<len[1][cur].d+e[i].d) 55 len[0][v].d=len[1][cur].d+e[i].d,len[0][v].id=cur; 56 else if(len[1][v].d < len[1][cur].d+e[i].d) 57 len[1][v].d=len[1][cur].d+e[i].d,len[1][v].id=cur; 58 }else{ 59 if(len[0][v].d<len[0][cur].d+e[i].d) 60 len[0][v].d=len[0][cur].d+e[i].d,len[0][v].id=cur; 61 } 62 dfs2(cur,e[i].v); 63 } 64 } 65 int get_big(int L,int R) 66 { 67 int range=R-L+1,i,lft,rgt,k=0; 68 for(i=1;(1<<i)<range;i++)k++; 69 lft=rbq[L][k]; 70 rgt=rbq[R-(1<<k)+1][k]; 71 if(l[lft]>l[rgt])return lft; 72 return rgt; 73 } 74 int get_sml(int L,int R) 75 { 76 int range=R-L+1,i,lft,rgt,k=0; 77 for(i=1;(1<<i)<range;i++) 78 k++; 79 lft=rsq[L][k]; 80 rgt=rsq[R-(1<<k)+1][k]; 81 if(l[lft]<l[rgt])return lft; 82 return rgt; 83 } 84 int query(int Q) 85 { 86 int i; 87 int res=1,big,sml; 88 frt=1,ral=1; 89 for(i=2;i<=n;i++){ 90 while(frt<=ral) 91 { 92 big=get_big(frt,ral); 93 sml=get_sml(frt,ral); 94 if(abs(l[big]-l[i])<=Q && abs(l[sml]-l[i])<=Q)break; 95 96 if(abs(l[big]-l[i])>Q)frt=big+1; 97 if(frt<=sml && abs(l[sml]-l[i])>Q)frt=sml+1; 98 } 99 ral=i; 100 res=max(res,ral-frt+1); 101 } 102 return res; 103 } 104 void init_rmq() 105 { 106 int i,j; 107 for(i=1;i<=n;i++)rbq[i][0]=i,rsq[i][0]=i; 108 for(j=1;(1<<j)<=n;j++) 109 for(i=1;i+(1<<j)-1<=n;i++){ 110 int lft,rgt; 111 lft=rbq[i][j-1]; 112 rgt=rbq[i+(1<<(j-1))][j-1]; 113 if(l[lft]>l[rgt])rbq[i][j]=lft; 114 else rbq[i][j]=rgt; 115 116 lft=rsq[i][j-1]; 117 rgt=rsq[i+(1<<(j-1))][j-1]; 118 if(l[lft]<l[rgt])rsq[i][j]=lft; 119 else rsq[i][j]=rgt; 120 } 121 } 122 int main() 123 { 124 int q,i; 125 // freopen("test.txt","r",stdin); 126 while(scanf("%d%d",&n,&m)!=EOF) 127 { 128 if(n+m==0)break; 129 input(); 130 memset(len,0,sizeof(len)); 131 dfs1(0,1); 132 dfs2(0,1); 133 for(i=1;i<=n;i++)l[i]=len[0][i].d; 134 init_rmq(); 135 while(m--){ 136 int res; 137 scanf("%d",&q); 138 res=query(q); 139 printf("%d\n",res); 140 } 141 } 142 return 0; 143 }
题目要求对给定的序列造一颗二叉搜索树,然后中序遍历得到o1串,然后kmp得到模式匹配结果.
此题的难点是造树与中序遍历,因为规模很大,朴素的方法造树会超时,而用递归遍历会爆栈.
将中序遍历写成非递归的形式,可以解决爆栈的问题,下面重点解决快速造树的问题.
设新加入的点值为b;
已插入的小于b的最大值为a;
已插入的大于b的最小值为c.
那么a,c中一个必定是b的父节点.
根据二叉搜索树的查找方式可以证明:
最后b所查找的区间是[a , c],所以a,c中一个必定会成为b的父亲.
下面证明a,c中只有一个能成为b的父亲:
a,c必定是父子关系,否则设他们的 LCA 为 mid
若mid>b,因为b<mid<c,与c是大于b中最小的数矛盾;
同理若mid<b,会与a发生矛盾.
因此a,c必定为父子关系.
如果a为c父亲,b,c都为它右孩子,所以a无法成为b的父亲;
如果c为父亲,a,b都为它左孩子,所以c无法成为b的父亲.
因此用线段树在每次插入b的时候用log(n)的代价找到a,c就可以了.
1 #include<stdio.h> 2 #include<string.h> 3 #define maxn 600006 4 #define maxl 7007 5 #define max(a,b) (a)>(b)?(a):(b) 6 #define min(a,b) (a)<(b)?(a):(b) 7 using namespace std; 8 typedef struct binary_tree{ 9 int ls;int rs; 10 }binary_tree; binary_tree bt[maxn]; 11 int lft[maxn<<2],rgt[maxn<<2]; 12 int n,pre[maxn],root,nxt[maxn],vis[2][maxn],nt; 13 char str[maxl]; 14 char txt[maxn*2]; 15 void build(int l,int r,int cur) 16 { 17 int m=(l+r)>>1; 18 lft[cur]=maxn;rgt[cur]=-1; 19 if(l==r)return; 20 build(l,m,cur<<1); 21 build(m+1,r,cur<<1|1); 22 } 23 void PushUp(int cur) 24 { 25 lft[cur]=min(lft[cur<<1],lft[cur<<1|1]); 26 rgt[cur]=max(rgt[cur<<1],rgt[cur<<1|1]); 27 } 28 void inserter(int d,int l,int r,int cur) 29 { 30 int m=(l+r)>>1; 31 if(l==r){ 32 lft[cur]=l; 33 rgt[cur]=l; 34 return; 35 } 36 if(d<=m)inserter(d,l,m,cur<<1); 37 else inserter(d,m+1,r,cur<<1|1); 38 PushUp(cur); 39 } 40 int query_big(int L,int R,int l,int r,int cur) 41 { 42 int m=(l+r)>>1,res=-1; 43 if(L>R)return -1; 44 if(L<=l && r<=R) 45 return rgt[cur]; 46 if(m<R)res=query_big(L,R,m+1,r,cur<<1|1); 47 if(res!=-1)return res; 48 if(l<=m)res=query_big(L,R,l,m,cur<<1); 49 return res; 50 } 51 int query_small(int L,int R,int l,int r,int cur) 52 { 53 int m=(l+r)>>1,res=maxn; 54 if(L>R)return maxn; 55 if(L<=l && r<=R) 56 return lft[cur]; 57 if(L<=m)res=query_small(L,R,l,m,cur<<1); 58 if(res!=maxn)return res; 59 if(R>m)res=query_small(L,R,m+1,r,cur<<1|1); 60 return res; 61 } 62 void input() 63 { 64 int i,a,b,d; 65 memset(bt,-1,sizeof(bt)); 66 scanf("%d",&n); 67 build(1,n,1); 68 scanf("%d",&d); 69 root=d; 70 inserter(d,1,n,1); 71 for(i=2;i<=n;i++){ 72 scanf("%d",&d); 73 a=query_small(d+1,n,1,n,1); 74 b=query_big(1,d-1,1,n,1); 75 // printf("\na=%d b=%d\n",a,b); 76 inserter(d,1,n,1); 77 if(a==maxn) 78 bt[b].rs=d; 79 else if(b==-1) 80 bt[a].ls=d; 81 else 82 { 83 if(bt[a].ls==-1)bt[a].ls=d; 84 else bt[b].rs=d; 85 } 86 } 87 scanf("%s",str); 88 } 89 void rcd(int cur) 90 { 91 if(cur%2)txt[nt++]='1'; 92 else txt[nt++]='0'; 93 } 94 void init() 95 { 96 int cur=root; 97 memset(pre,0,sizeof(pre)); 98 memset(vis,0,sizeof(vis)); 99 pre[root]=0;nt=0; 100 101 while(cur){ 102 rcd(cur); 103 if(bt[cur].ls!=-1 && !vis[0][cur]){ 104 vis[0][cur]=1; 105 pre[bt[cur].ls]=cur; 106 cur=bt[cur].ls; 107 }else if(bt[cur].rs!=-1 && !vis[1][cur]){ 108 vis[1][cur]=1; 109 pre[bt[cur].rs]=cur; 110 cur=bt[cur].rs; 111 } 112 else cur=pre[cur]; 113 } 114 } 115 void kmp() 116 { 117 int i,j=-1,len,cnt=0; 118 len=strlen(str); 119 nxt[0]=-1; 120 for(i=0;i<len;){ 121 while(j!=-1 && str[i]!=str[j])j=nxt[j]; 122 i++;j++; 123 nxt[i]=j; 124 } 125 126 for(i=0,j=0;i<nt;i++){ 127 while(j!=-1 && str[j]!=txt[i])j=nxt[j]; 128 j++; 129 if(j==len)cnt++; 130 } 131 printf("%d\n",cnt); 132 } 133 int main() 134 { 135 int cas,t; 136 scanf("%d",&cas); 137 for(t=1;t<=cas;t++) 138 { 139 printf("Case #%d: ",t); 140 input(); 141 init(); 142 kmp(); 143 } 144 return 0; 145 }
给出一个无向图,处理m次询问:
修改图中一条边的权值,求此时该图的最小生成树的权值v,统计v.
最后输出m个v的平均值,每次询问是独立的,并且边权只会改大.
求m次mst显然会超时.
每次都只修改一条边,要找到一种办法从原本的mst中找到新的mst的权值.
如果修改的边不在原来mst上,那么mst必定是不变的.
如果修改的边e(u,v)在原来的mst上,删去e,mst分成两棵子树Su,Sv,
求得两颗子树的最短距离d,就是新加的边使它们连成mst.
最短距离可以用树形DP得到,
定义dp[i][rt]为 以i为根节点的树中,i到rt为根的子树的最短距离.
dp[i][rt]=min{
dp[i][son[rt]];
}
初始化dp[i][rt]为(i,rt)在邻接表里的距离,遍历一遍以i为根的子树,
就可以更新好所有的dp[i][],复杂度为n2.
查询的时,不妨设设u为v的父亲,最短距离为
min{
dp[son[v]][u];
}
我曾很纠结,为什么找到的最短边加上去了就会连成mst,要知道mst的形态有很多种啊,
换种形态也许会得到更小的树.
后来发现很好想通,对于任意一棵树,如果加上一条边就会得到一个环,此时环上的任意一条边
都能删掉,为了得到最小生成树,我们当然要删掉边长最大的一条边......不断这样操作,如果最后
无法再删边了,那这棵树就是最小生成树了.
我们修改掉e(u,v)后,再找一条最小的边E连上,相当于进行了一次删边操作,如果它现在是一颗mst
那么必定无法再找出可删边,下面证明:
首先如果有可删边,它只可能是 新加一条边e后在与E构成的环中,此时环中最大的边不可能是E,否则与E最小矛盾
也不可能是原树上的边,否则与原树是mst矛盾,因此最大的边只可能是e.
1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 #include<vector> 5 #define maxn 3003 6 using namespace std; 7 typedef long long llong; 8 struct Edge{ 9 int u,v,d; 10 }; 11 struct nd{ 12 int v,d; 13 }; 14 const int inf=1000000010; 15 vector<Edge> e; 16 vector<nd> tre[maxn]; 17 int f[maxn][maxn],rcd[maxn][maxn]; 18 int fa[maxn],from[maxn],len[maxn]; 19 int n,m,mst; 20 21 void input() 22 { 23 int i,j; 24 Edge add; 25 for(i=0;i<n;i++) 26 for(j=0;j<n;j++)f[i][j]=inf; 27 for(i=0;i<m;i++){ 28 scanf("%d%d%d",&add.u,&add.v,&add.d); 29 e.push_back(add); 30 f[add.u][add.v]=add.d; 31 f[add.v][add.u]=add.d; 32 } 33 } 34 bool cmp(const Edge& a,const Edge& b){ 35 return a.d < b.d; 36 } 37 int find(int cur) 38 { 39 if(fa[cur]==cur)return cur; 40 return fa[cur]=find(fa[cur]); 41 } 42 void MST() 43 { 44 int i,u,v,fu,fv,add=0; 45 nd tmp; 46 for(i=0;i<n;i++)fa[i]=i; 47 sort(e.begin(),e.end(),cmp); 48 for(i=0,mst=0;i<m;i++){ 49 u=e[i].u;v=e[i].v; 50 fu=find(u);fv=find(v); 51 if(fv != fu){ 52 fa[fv]=fa[fu]; 53 mst+=e[i].d; 54 f[u][v]=inf; 55 f[v][u]=inf; 56 rcd[u][v]=-1; 57 rcd[v][u]=-1; 58 tmp.v=v;tmp.d=e[i].d; 59 tre[u].push_back(tmp); 60 61 tmp.v=u;tmp.d=e[i].d; 62 tre[v].push_back(tmp); 63 add++; 64 if(add==n-1)break; 65 } 66 } 67 e.clear(); 68 } 69 void init_tree(int fa,int cur) 70 { 71 int i,v; 72 for(i=0;i<tre[cur].size();i++) 73 if(tre[cur][i].v != fa){ 74 v=tre[cur][i].v; 75 from[v]=cur; 76 len[v]=tre[cur][i].d; 77 init_tree(cur,v); 78 } 79 } 80 int dfs(int rt,int fa,int cur) 81 { 82 int i,tmp; 83 for(i=0;i<tre[cur].size();i++) 84 if(tre[cur][i].v != fa){ 85 tmp=dfs(rt,cur,tre[cur][i].v); 86 f[cur][rt]=min(tmp,f[cur][rt]); 87 } 88 return f[cur][rt]; 89 } 90 int find_edge(int rt,int fa,int cur) 91 { 92 int i,res,tmp; 93 res=f[rt][cur]; 94 for(i=0;i<tre[cur].size();i++) 95 if(tre[cur][i].v!=fa){ 96 tmp=find_edge(rt,cur,tre[cur][i].v); 97 res=min(res,tmp); 98 } 99 return res; 100 } 101 void solv() 102 { 103 int q,u,v,d,w,tq; 104 llong cnt=0; 105 double ans; 106 scanf("%d",&q); 107 tq=q; 108 while(q--){ 109 scanf("%d%d%d",&u,&v,&d); 110 if(rcd[u][v]==0)cnt+=mst; 111 else{ 112 if(rcd[u][v]!=-1){ 113 w=rcd[u][v]; 114 if(w>d)w=d; 115 if(from[u]==v)cnt+=mst+w-len[u]; 116 else cnt+=mst+w-len[v]; 117 } 118 else{ 119 if(from[u]==v){ 120 w=find_edge(v,v,u); 121 rcd[u][v]=w; 122 if(w>d)w=d; 123 cnt+=mst+w-len[u]; 124 } 125 else{ 126 w=find_edge(u,u,v); 127 rcd[u][v]=w; 128 if(w>d)w=d; 129 cnt+=mst+w-len[v]; 130 } 131 } 132 } 133 } 134 ans=(double)cnt/(double)tq; 135 printf("%.4f\n",ans); 136 } 137 void flush() 138 { 139 int i; 140 for(i=0;i<n;i++)tre[i].clear(); 141 } 142 int main() 143 { 144 // freopen("test","r",stdin); 145 while(scanf("%d%d",&n,&m)) 146 { 147 if( n+m == 0)break; 148 input(); 149 memset(rcd,0,sizeof(rcd)); 150 MST(); 151 memset(from,-1,sizeof(from)); 152 memset(len,0,sizeof(len)); 153 init_tree(-1,0); 154 for(int i=0;i<n;i++) 155 dfs(i,-1,i); 156 solv(); 157 flush(); 158 } 159 return 0; 160 }
总的来说,福州这套题综合性蛮强的,尤其是对数据结构的要求很高.