【小结】福州现场赛题

  B.Alice's mooncake shop

  商店在开业的m个小时中会接到一些订单,给出每个小时生产的代价,产品保存周期,和产品保存1小时的代价,求完成所有订单的最小消耗.

  算出所有订单的时间戳,预处理出1<=t<=m小时做出来的月饼保存到m的代价cost[t],

  假设现在需要知道订单 i 的最优代价,那么查找它的时间戳 time[i]与它之前t小时的代价中最小

  的就可以了,需要用到rmq.

   

Alice's mooncake shop
  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 }

 

  C.Bob’s Race

  给出一颗带边权的树,先求从每个点出发的最长路径,然后处理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).

  

Bob’s Race
  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 }

 

  E.Moles

  题目要求对给定的序列造一颗二叉搜索树,然后中序遍历得到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就可以了.

  

Moles
  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 }

 

  F.Genghis Khan the Conqueror

  给出一个无向图,处理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.

  

Genghis Khan the Conqueror
  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 }

 

 

 总的来说,福州这套题综合性蛮强的,尤其是对数据结构的要求很高.

你可能感兴趣的:(小结)