• HNOI2015 Day 2题解


    昨天做了HNOI day 2,感觉好像还是可做的,想当年什么splay还是高级算法,现在点剖什么就老考了简直丧病,虽然第二题还没写那就先当下嘴巴选手吧= =

    T1:[HNOI2015]落忆枫音

    描述:http://www.lydsy.com/JudgeOnline/problem.php?id=4011

    这道题还是挺神奇的,首先如果是个DAG的话我们可以把所有点的入度相乘就得到答案。

    那么现在加上了一条边x->y,也就是说答案=原来的答案+用上这条边的答案,

    我们可以发现,如果我们把x到根的链拿出来,其他点其实和DAG图上一样也可以入度直接相乘而得到答案,也就是S/prod(in[x])x为链上的点,可以发现如果记f[x]为所以到x的链上的1/prod(in[x])(也就是逆元啦)之和,我们可以用拓扑序来进行dp维护,就可以解决本题啦

    我打的有点复杂,我是先把整个图的入度相乘然后再删去不合法的情况,也就是找y->x的链上的1/prod(in[x])之和,比较难打,但是总体思想差不多也就这样啦。

    CODE:

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 typedef long long ll;
     7 #define maxn 200100
     8 struct edges{
     9     int to,next;
    10 }edge[maxn];
    11 int next[maxn],l;
    12 ll f[maxn];
    13 inline void addedge(int x,int y) {
    14     edge[++l]=(edges){y,next[x]};next[x]=l;
    15 }
    16 int q[maxn],in[maxn];
    17 inline void bfs() {
    18     q[1]=1;
    19     for (int l=1,r=1,u=1;l<=r;u=q[++l]) 
    20         for (int i=next[u];i;i=edge[i].next) {
    21             in[edge[i].to]--;
    22             if (in[edge[i].to]==0) q[++r]=edge[i].to;
    23         }
    24 }
    25 #define mod 1000000007
    26 inline int power(ll x,int y) {
    27     ll ans=1;
    28     for (;y;y>>=1) {
    29         if (y&1) (ans*=x)%=mod;
    30         (x*=x)%=mod;
    31     }
    32     return ans;
    33 }
    34 int n;
    35 int a[maxn];
    36 inline void dp(int x,int y){
    37     for (int r=n,u=q[n];r;u=q[--r]) {
    38         for (int i=next[u];i;i=edge[i].next) (f[u]+=f[edge[i].to])%=mod;
    39         if (u==y) f[u]=1;
    40         (f[u]*=power(a[u],mod-2))%=mod;
    41     }
    42 }
    43 int main(){
    44     int m,x,y;
    45     freopen("maple.in","r",stdin);
    46     freopen("maple.out","w",stdout);
    47     scanf("%d%d%d%d",&n,&m,&x,&y);
    48     for (int i=1;i<=m;i++) {
    49         int u,v;
    50         scanf("%d%d",&u,&v);
    51         addedge(u,v);
    52         in[v]++;
    53     }
    54     if (y==1||x==y) {
    55         ll ans=1;
    56         for (int i=2;i<=n;i++) (ans*=in[i])%=mod;
    57         printf("%lld
    ",ans);
    58         return 0;
    59     }
    60     for (int i=1;i<=n;i++) a[i]=in[i];
    61     a[y]++;
    62     ll ans=1;
    63     for (int i=2;i<=n;i++) (ans*=a[i])%=mod;
    64     bfs();
    65     dp(y,x);
    66     printf("%lld
    ",(ans-ans*f[y]%mod+mod)%mod);
    67     return 0;
    68 }
    69     
    View Code

    T2:[HNOI2015]开店

    描述:http://www.lydsy.com/JudgeOnline/problem.php?id=4012

    又是CLJ的神题,数据结构一般都是被很多数据结构大神用各种方法秒,吾等蒟蒻只能被秒了= =

    解法一:

    首先有一种比较高端的做法,就是用线段树那样维护虚树,每个节点维护l~r的虚树(一共log n层每次直接nlogn暴力重构一共O(n log^2 n)),那么求答案可以在每棵虚数中单独求解。

    接下来我们考虑如何求答案。对虚树上的节点分别记录子树中点的距离和down,子树外点的距离和up,以及子树中的节点数size。对于每一个查询,我们先找到在dfs序中比他前的点以及比他后的点,选择lca深度较大的点进行转移(因为这样才不会影响答案)。记从点x转移到点y,那么答案为down+up+(dist[y]-dist[x])*size-(dist[y]-dist[x])*(tot-size)(dist为该点的深度,tot为总节点树)

    解法二:

    我们可以先点剖一下然后考虑用可持久化线段树维护,对于每个点他至多被log n个重心覆盖,我们分别对每个重心ai进行查询,要求路径x,y必须穿过重心ai,记i的子树中l~r的节点数为count[i],dist[u]为u到重心的距离,sum[i]表示i子树中l~r的节点的dist之和,那么该子树ai的答案为count[ai]*dist[u]+sum[x]-count[y]*dis[u]-sum[y], 其中y为包含u的x的儿子节点,那么我们只需对重心已经他们的儿子节点维护一个线段树即可。

    但是这样会爆空间,注意到每个点的儿子最多只有3个,那么我们可以直接维护他的儿子节点,不用维护其儿子节点即可

    UPD:其实不用线段树的= =,开个vector记录子树节点然后排下序用前缀和,然后每次二分查询就行,感觉特别STL,初始化部分之间可以照搬CLJ ZJOI2015幻想乡战略游戏,几乎一模一样,写完不用3kb= =

    两种做法时间均为O(N log^2 N)

    CODE:

      1 #include<cstdio>
      2 #include<iostream>
      3 #include<cstring>
      4 #include<algorithm>
      5 #include<vector>
      6 using namespace std;
      7 #define maxn 150100
      8 typedef long long ll;
      9 typedef pair<ll,ll> ii;
     10 typedef pair<ll,ii> iii;
     11 #define fi first
     12 #define se second
     13 #define pb push_back
     14 struct edges{int to,next,dist;}edge[maxn*2];
     15 int l,next[maxn];
     16 inline void addedge(int x,int y,int z) {
     17     edge[++l]=(edges){y,next[x],z};next[x]=l;
     18     edge[++l]=(edges){x,next[y],z};next[y]=l;
     19 }
     20 bool done[maxn];
     21 int s[maxn],f[maxn],root,size;
     22 void dfs(int u,int fa){
     23     s[u]=1;f[u]=0;
     24     int v=0;
     25     for (int i=next[u];i;i=edge[i].next) {
     26         if (done[v=edge[i].to]||v==fa) continue;
     27         dfs(v,u);
     28         s[u]+=s[v];
     29         f[u]=max(f[u],s[v]);
     30     }
     31     f[u]=max(f[u],size-s[u]);
     32     if (f[u]<f[root]) root=u;
     33 }
     34 vector<iii> pre[maxn];
     35 vector<ii> date[maxn][3];
     36 int a[maxn];
     37 void getdist(int u,int fa,int tar,int dist,int cnt) {
     38     pre[u].pb(iii(tar,ii(dist,cnt)));
     39     date[tar][cnt].pb(ii(a[u],dist));
     40     s[u]=1;int v=0;
     41     for (int i=next[u];i;i=edge[i].next) {
     42         if (done[v=edge[i].to]||v==fa) continue;
     43         getdist(v,u,tar,dist+edge[i].dist,cnt);
     44         s[u]+=s[v];
     45     }
     46 }
     47 void work(int u){
     48     done[u]=1;
     49     int v=0;
     50     pre[u].pb(iii(u,ii(0,3)));
     51     int cnt=0;
     52     for (int i=next[u];i;i=edge[i].next) {
     53         if (done[v=edge[i].to]) continue;
     54         getdist(v,0,u,edge[i].dist,cnt);
     55         f[0]=size=s[v];
     56         dfs(v,root=0);
     57         work(root);
     58         cnt++;
     59     }
     60 }
     61 ll query(vector<ii> *a,ii date,int l,int r) {
     62     ll ans=0;
     63     for (int i=0;i<=2;i++) {
     64         if (i==date.se) continue;
     65         int L=lower_bound(a[i].begin(),a[i].end(),ii(l,-1))-a[i].begin(),
     66             R=lower_bound(a[i].begin(),a[i].end(),ii(r+1,-1))-a[i].begin();
     67         if (L) ans-=date.fi*L+a[i][L-1].se;
     68         if (R) ans+=date.fi*R+a[i][R-1].se;
     69     }
     70     return ans;
     71 }
     72 int main(){
     73     freopen("shop.in","r",stdin);
     74     freopen("shop.out","w",stdout);
     75     int n,Q,_;
     76     scanf("%d%d%d",&n,&Q,&_);
     77     for (int i=1;i<=n;i++) scanf("%d",a+i);
     78     for (int i=1;i<n;i++) {
     79         int x,y,z;
     80         scanf("%d%d%d",&x,&y,&z);
     81         addedge(x,y,z);
     82     }
     83     f[0]=size=n;
     84     dfs(1,root=0);
     85     work(root);
     86     for (int i=1;i<=n;i++) 
     87         for (int j=0;j<3;j++) {
     88             sort(date[i][j].begin(),date[i][j].end());
     89             for (int k=1;k<date[i][j].size();k++) 
     90                 date[i][j][k].se+=date[i][j][k-1].se;
     91         }
     92     ll ans=0;
     93     while (Q--) {
     94         int u,x,y;
     95         scanf("%d%d%d",&u,&x,&y);
     96         int l=min((ans+x)%_,(ans+y)%_),r=max((ans+x)%_,(ans+y)%_);
     97         ans=0;
     98         for (int i=0;i<pre[u].size();i++) {
     99             if (l<=a[pre[u][i].fi]&&a[pre[u][i].fi]<=r) ans+=pre[u][i].se.fi;
    100             ans+=query(date[pre[u][i].fi],pre[u][i].se,l,r);
    101         }
    102         printf("%lld
    ",ans);
    103     }
    104     return 0;
    105 }
    View Code

    T3:[HNOI2015]实验比较

    描述:http://www.lydsy.com/JudgeOnline/problem.php?id=4013

    首先对于这道题,我们可以发现如果u=v,那么我们可以将其两个点缩为同一个点,若u<v,那么我们可以连一条u向v的边,可以证明最后的图上每个联通块一定是一个环或者树。

    如果是一个环的话答案显然为零。如果是一棵树的话,我们考虑使用树形dp,如果我们已知所有子树的答案,如何更新这棵树。

    观察一个序列,我们可以发现该序列总是被若干个<分割成若干块,那么我们可以记f[i][j]为以i 为根节点的子树序列中有j个块的方案数一共有多少种,可得对两个不相干的树x,y结合的话,记答案为g[k],可得f[x][i],f[y][j]对g[k]的贡献为C(k,i)*C(i,i+j-k)*f[x][i]*f[y][j](可以这样认为,i个人先占据i个方格,那么剩下k-i个方格必须由j个人去占领,所以最后i+j-k个人就必须与之前的i个人一起了)

    每次更新的时间复杂度为O(n^3),总的时间复杂度为O(n^4),可以通过这道题

    CODE:

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<vector>
     6 using namespace std;
     7 #define maxn 110
     8 #define mod 1000000007
     9 vector<int> e[maxn];
    10 #define pb push_back
    11 int fa[maxn];
    12 int find(int x) {return fa[x]==x?fa[x]:fa[x]=find(fa[x]);}
    13 int c[maxn][maxn]; 
    14 inline void init(){
    15     for (int i=0;i<=100;i++) {
    16         c[i][0]=1;
    17         for (int j=1;j<=i;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
    18     }
    19 }
    20 struct info{
    21     int level,f[maxn];
    22     info(){level=0;memset(f,0,sizeof(f));}
    23     inline int sum(){
    24         int ans=0;
    25         for (int i=1;i<=level;i++) (ans+=f[i])%=mod;
    26         return ans;
    27     }
    28 };
    29 inline info update(info x,info y) {
    30     info ans;
    31     for (int i=1;i<=x.level;i++) {
    32         for (int j=1;j<=y.level;j++) {
    33             for (int k=max(i,j);k<=i+j;k++) {
    34                 (ans.f[k]+=c[k][i]*1ll*c[i][i+j-k]%mod*x.f[i]%mod*y.f[j]%mod)%=mod;
    35             }
    36         }
    37     }
    38     ans.level=x.level+y.level;
    39     return ans;
    40 }
    41 bool b[maxn];
    42 info dfs(int x) {
    43     info ans;
    44     b[x]=1;
    45     for (int i=0;i<e[x].size();i++) 
    46         if (i==0) ans=dfs(e[x][i]);
    47         else ans=update(ans,dfs(e[x][i]));
    48     for (int i=ans.level;i;i--) ans.f[i+1]=ans.f[i];
    49     ans.f[1]=ans.level==0;
    50     ans.level++;
    51     return ans;    
    52 }
    53 
    54 int main(){
    55     freopen("pairwise.in","r",stdin);
    56     freopen("pairwise.out","w",stdout);
    57     int n,m;
    58     init();
    59     scanf("%d%d",&n,&m);
    60     for (int i=1;i<=n;i++) fa[i]=i;
    61     static int from[maxn],to[maxn];
    62     static char opt[maxn][2];
    63     for (int i=1;i<=m;i++) {
    64         scanf("%d%s%d",from+i,opt[i],to+i);
    65         if (opt[i][0]=='=') fa[find(from[i])]=find(to[i]);
    66     }
    67     static int id[maxn],in[maxn];
    68     for (int i=1;i<=n;i++) id[i]=find(i);
    69     for (int i=1;i<=m;i++)
    70         if (opt[i][0]=='<') {
    71             e[id[from[i]]].pb(id[to[i]]);
    72             in[id[to[i]]]++;
    73         }
    74     info ans;
    75     for (int i=1;i<=n;i++){
    76         if (!b[id[i]]&&in[id[i]]==0) ans=ans.level==0?dfs(id[i]):update(ans,dfs(id[i]));
    77     }
    78     for (int i=1;i<=n;i++)
    79         if (!b[id[i]]) {printf("%d
    ",0);return 0;}
    80     printf("%d
    ",ans.sum());
    81     return 0;
    82 }
    View Code
  • 相关阅读:
    Oracle第五周测验
    软件测试第五周
    Oracle第四周作业
    c++第二章测试
    软件测试第四章
    软件测试 第三章
    Centos 安装.NET Core环境
    .net core 集成极光推送
    Swagger添加文件上传测试
    linux firewall
  • 原文地址:https://www.cnblogs.com/New-Godess/p/4459765.html
Copyright © 2020-2023  润新知