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
原文地址:https://www.cnblogs.com/New-Godess/p/4459765.html