Codeforces Bubble Cup 8 - Finals [Online Mirror] 解题报告

来源:互联网 发布:java开发物流管理系统 编辑:程序博客网 时间:2024/05/17 04:29

D Tablecity

        从一头往另一头扫一遍,再逆着回来就好了。因为贼每走一步,横坐标的奇偶性都会改变。

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;int i;int main(){printf("2000\n");for (i=1;i<=1000;i++) printf("%d 1 %d 2\n",i,i);for (i=1000;i>0;i--) printf("%d 1 %d 2\n",i,i);return 0;}

H Bots

        每走一步,树增加了一层,节点数是上一层的2倍。但是到了第n+1层开始,就会出现红/蓝边数量达到n的情况,这样的节点不能翻倍,计算一下组合数把这种情况减去就行了。算组合数的时候递推计算,用到了逆元。

#include <iostream> #include <cstdio> #include <algorithm> #include <set>#include <vector>#include <string.h>using namespace std; #define ll long longconst int mod=1000000007;void ExEuclid(ll a,ll b,ll &x,ll &y,ll &q){        if(b==0){            x=1;y=0;q=a;            return;        }        ExEuclid(b,a%b,y,x,q);        y-=x*(a/b);    }        ll inv(ll num){        ll x,y,q;        ExEuclid(num,mod,x,y,q);        if(q==1)return (x+mod)%mod;    }  int main(){int n;while(cin>>n){ll ans=1;ll cur=1;ll C=1;for(int i=1;i<=n*2;i++){cur*=2;cur%=mod;if(i>n){cur-=2*C;cur+=mod;cur+=mod;cur%=mod;C*=i;C%=mod;C*=inv(i-n);C%=mod;}ans+=cur;ans%=mod;}cout<<ans<<endl;}return 0;}

F Bulbo

        首先有一个可以推出来的结论,你只移动到输入数据中出现的那些位置,可以得到最优解(不需要移动到数据中没有出现的位置去)。所以就可以用离散化+dp+滚动数组来解决了。dp(i,j)表示第i轮时,位置在j(离散化后)的最优解(第一维滚动只开2)。注意状态转移时,本应是多个状态转移到一个状态,这样复杂度太高会超时,需要利用递推维护多个状态的最优值,一次性转移到一个状态。

        顺便说一下,后来发现这个题可以贪心,每次维护一个范围,每次开始前移动到这个范围内,灯亮,花费为最少。

#include <iostream> #include <cstdio> #include <algorithm> #include <set>#include <vector>#include <string.h>using namespace std; #define ll long longconst int mod=1000000007;ll INF=1000000000000000000LL;int l[5010];int r[5010];ll dp[2][100010];int pos[100010];int k;int main(){int n,x;while(cin>>n>>x){memset(dp,-1,sizeof(dp));ll ans=INF;k=0;pos[k++]=x;for(int i=1;i<=n;i++){scanf("%d%d",&l[i],&r[i]);pos[k++]=l[i];pos[k++]=r[i];}sort(pos,pos+k);k=unique(pos,pos+k)-pos;for(int i=0;i<k;i++){dp[0][i]=abs(x-pos[i]);}for(int i=1;i<=n;i++){ll part2=INF;for(int j=0;j<k;j++){dp[i%2][j]=INF;ll part1=0;if(pos[j]<l[i]){part1=l[i]-pos[j];}else if(pos[j]>r[i]){part1=pos[j]-r[i];}if(j>0){part2=min(part2+pos[j]-pos[j-1],dp[(i-1)%2][j]);}else{part2=dp[(i-1)%2][j];}dp[i%2][j]=min(dp[i%2][j],part1+part2);}part2=INF;for(int j=k-1;j>=0;j--){ll part1=0;if(pos[j]<l[i]){part1=l[i]-pos[j];}else if(pos[j]>r[i]){part1=pos[j]-r[i];}if(j<k-1){part2=min(part2+pos[j+1]-pos[j],dp[(i-1)%2][j]);}else{part2=dp[(i-1)%2][j];}dp[i%2][j]=min(dp[i%2][j],part1+part2);}}for(int i=0;i<k;i++){ans=min(ans,dp[n%2][i]);}cout<<ans<<endl;}return 0;}

B Bribes

        首先为了迅速求得树上许多点对之间的距离,需要使用nlogn的在线LCA(倍增法)。然后需要知道每条单向边被逆行了多少次,这时可用打标记的方法,起点+1终点-1,又由于它是树型结构,统计时用dfs。最后的计算利用了等比数列求和公式和快速幂。

#include <iostream> #include <cstdio> #include <algorithm> #include <set>#include <vector>#include <string.h>using namespace std; #define ll long longconst int mod=1000000007;ll INF=1000000000000000000LL;#define max_size 100010  ll Quick_Pow(ll a,ll n){     ll ret=1;      ll temp=a%mod;      while (n){          if (n&1) ret=ret*temp%mod;          temp=temp*temp%mod;          n>>=1;      }      return ret;  }  int d[max_size],p[max_size][18];  int head[max_size];  int cnt;int a[max_size];int b[max_size];int x[max_size];bool downpoint[max_size];bool uppoint[max_size];int downres[max_size];int upres[max_size];bool vis2[max_size];struct Edge{      int v;      int pre;  }eg[max_size];    void add(int x,int y){     eg[cnt].v=y;      eg[cnt].pre=head[x];      head[x]=cnt++;  }    void dfs(int k){     int m,x,i,j;      for(i=head[k];i!=0;i=eg[i].pre){          x=eg[i].v;          p[x][0]=k;          m=k;          d[x]=d[k]+1;          for(j=0;p[m][j]!=0;j++){              p[x][j+1]=p[m][j];            m=p[m][j];          }          dfs(x);      }  }    int find_lca(int x,int y){      int m,k;      if(x==y)return x;      if(d[x]<d[y]){          m=x;          x=y;          y=m;      }      m=d[x]-d[y];      k=0;      while(m){        if(m&1)          x=p[x][k];          m>>=1;          k++;      }      if(x==y)return x;      k=0;      while(x!=y){          if(p[x][k]!=p[y][k]||p[x][k]==p[y][k]&&k==0){              x=p[x][k];              y=p[y][k];              k++;          }          else{              k--;          }      }      return x;  }struct oriEdge{    int v;    int flag;    oriEdge(int v,int flag):v(v),flag(flag){    }    oriEdge(){    }};vector<oriEdge> E[max_size];  bool vis[max_size];  int siz[max_size];  int predfs(int u){    vis[u]=1;      int sz=E[u].size();      siz[u]=1;      for(int i=0;i<sz;i++){          int v=E[u][i].v;          if(!vis[v]){              if(E[u][i].flag==0){                downpoint[v]=1;            }            if(E[u][i].flag==1){                uppoint[v]=1;            }            add(u,v);              siz[u]+=predfs(v);          }      }      return siz[u];  }  ll ans=0;int downcnt[max_size];int upcnt[max_size];void dfs2(int k){downcnt[k]+=downres[k];    upcnt[k]+=upres[k];    for(int i=head[k];i!=0;i=eg[i].pre){          int x=eg[i].v;        dfs2(x);        downcnt[k]+=downcnt[x];        upcnt[k]+=upcnt[x];    }    if(uppoint[k]){ans+=Quick_Pow(2,upcnt[k])-1;ans+=mod;ans%=mod;}if(downpoint[k]){ans+=Quick_Pow(2,downcnt[k])-1;ans+=mod;ans%=mod;}} int main(){    int n;    while(cin>>n){        cnt=1;        for(int i=1;i<n;i++){            scanf("%d%d%d",&a[i],&b[i],&x[i]);            if(!x[i])E[a[i]].push_back(oriEdge(b[i],2));             else E[a[i]].push_back(oriEdge(b[i],1));             if(!x[i])E[b[i]].push_back(oriEdge(a[i],2));             else E[b[i]].push_back(oriEdge(a[i],0));         }        predfs(1);        dfs(1);                int k;        cin>>k;        int Last=1;                for(int i=1;i<=k;i++){            int s;            scanf("%d",&s);            int LCA=find_lca(Last,s);            ll cur=0;                        if(Last!=LCA){                upres[Last]++;                upres[LCA]--;            }            if(s!=LCA){                downres[s]++;                downres[LCA]--;            }            Last=s;         }        dfs2(1);        cout<<ans<<endl;    }    return 0;}

G Run for beer

        注意题目给的数据范围,边的长度最长是9,但是每多经过一个点,速度变为1/10。也就是说,需要经过尽可能少的点。另外,边长度可以是0,也就是说,如果最后经过了一系列长度为0的边,是不会增加时间的。我的做法是多次bfs,先找出哪些点到终点可以全是0边,然后在这些点中,找一个离起点最近的,再贪心找到路径。实现比较挫,bfs了好多次,也跪了好多次,最后才过。。。

#include <iostream> #include <cstdio> #include <algorithm> #include <set>#include <stack>#include <vector>#include <queue>#include <string.h>using namespace std; #define ll long longconst int INF = 1000000000;const int maxm=100010;const int maxn=100010;int head[maxn];int pre[maxm<<1];int from[maxm<<1];int to[maxm<<1];int len[maxm<<1];int vis[maxn];int tote;int k;int lv[maxn];int _lv[maxn];bool finish[maxn];bool finish2[maxn];int cost[maxn];int lvcost[maxn];int fa[maxn];int _fa[maxn];void init(){memset(head,-1,sizeof(head));tote=0;k=0;}void addedge(int u,int v,int l){to[tote]=v;from[tote]=u;len[tote]=l;pre[tote]=head[u];head[u]=tote++;//to[tote]=u;from[tote]=v;len[tote]=l;pre[tote]=head[v];head[v]=tote++;}int ans[maxn];int path[maxn];void clearQueue(queue<int> &que){while(que.size())que.pop();}int print(int u){int re=0;if(!finish[u])printf("%d",cost[u]);if(u)re=print(from[fa[u]])+1;path[k++]=u;return re;}int main(){init();int n,m;cin>>n>>m;for(int i=1;i<=m;i++){int u,v,l;scanf("%d%d%d",&u,&v,&l);addedge(u,v,l);}queue<int> que;que.push(0);vis[0]=1;while(que.size()){int u=que.front();que.pop();for(int i=head[u];~i;i=pre[i]){int v=to[i];if(vis[v])continue;vis[v]=1;lv[v]=lv[u]+1;que.push(v);}}clearQueue(que);memset(vis,0,sizeof(vis));que.push(n-1);vis[n-1]=1;while(que.size()){int u=que.front();que.pop();finish[u]=1;for(int i=head[u];~i;i=pre[i]){int v=to[i];if(len[i])continue;//only 0 edgeif(vis[v])continue;vis[v]=1;_lv[v]=_lv[u]+1;que.push(v);_fa[v]=i;}}int MIN=INF;for(int i=0;i<n;i++){if(finish[i]){MIN=min(MIN,lv[i]);}}for(int i=0;i<n;i++){if(lv[i]!=MIN){finish[i]=0;}}clearQueue(que);memset(vis,0,sizeof(vis));for(int i=0;i<n;i++){lvcost[i]=INF;if(finish[i]){que.push(i);vis[i]=1;}else{cost[i]=INF;}}lvcost[MIN]=0;while(que.size()){int u = que.front();que.pop();if(lvcost[lv[u]]!=cost[u])continue;for(int i=head[u];~i;i=pre[i]){int v=to[i];if(lv[v]+1!=lv[u])continue;cost[v]=min(cost[v],len[i]);lvcost[lv[v]]=min(lvcost[lv[v]],cost[v]);if(vis[v])continue;vis[v]=1;que.push(v);}}clearQueue(que);memset(vis,0,sizeof(vis));que.push(0);vis[0]=1;while(que.size()){int u=que.front();que.pop();if(finish[u]){finish2[u]=1;}for(int i=head[u];~i;i=pre[i]){if(len[i]!=cost[u])continue;int v=to[i];if(vis[v])continue;vis[v]=1;que.push(v);fa[v]=i;}}int MIN2=INF;int target=-1;for(int i=0;i<n;i++){if(finish2[i]){if(_lv[i]<MIN2){MIN2=_lv[i];target=i;}}}if(print(target)==0){printf("0");}printf("\n");int cur=target;if(cur!=n-1)while(1){cur=from[_fa[cur]];path[k++]=cur;if(cur==n-1)break;}cout<<k<<endl;for(int i=0;i<k;i++){printf("%d ",path[i]);}return 0;}

0 0
原创粉丝点击