昨天做了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:

 #include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
#define maxn 200100
struct edges{
int to,next;
}edge[maxn];
int next[maxn],l;
ll f[maxn];
inline void addedge(int x,int y) {
edge[++l]=(edges){y,next[x]};next[x]=l;
}
int q[maxn],in[maxn];
inline void bfs() {
q[]=;
for (int l=,r=,u=;l<=r;u=q[++l])
for (int i=next[u];i;i=edge[i].next) {
in[edge[i].to]--;
if (in[edge[i].to]==) q[++r]=edge[i].to;
}
}
#define mod 1000000007
inline int power(ll x,int y) {
ll ans=;
for (;y;y>>=) {
if (y&) (ans*=x)%=mod;
(x*=x)%=mod;
}
return ans;
}
int n;
int a[maxn];
inline void dp(int x,int y){
for (int r=n,u=q[n];r;u=q[--r]) {
for (int i=next[u];i;i=edge[i].next) (f[u]+=f[edge[i].to])%=mod;
if (u==y) f[u]=;
(f[u]*=power(a[u],mod-))%=mod;
}
}
int main(){
int m,x,y;
freopen("maple.in","r",stdin);
freopen("maple.out","w",stdout);
scanf("%d%d%d%d",&n,&m,&x,&y);
for (int i=;i<=m;i++) {
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v);
in[v]++;
}
if (y==||x==y) {
ll ans=;
for (int i=;i<=n;i++) (ans*=in[i])%=mod;
printf("%lld\n",ans);
return ;
}
for (int i=;i<=n;i++) a[i]=in[i];
a[y]++;
ll ans=;
for (int i=;i<=n;i++) (ans*=a[i])%=mod;
bfs();
dp(y,x);
printf("%lld\n",(ans-ans*f[y]%mod+mod)%mod);
return ;
}

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:

 #include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define maxn 150100
typedef long long ll;
typedef pair<ll,ll> ii;
typedef pair<ll,ii> iii;
#define fi first
#define se second
#define pb push_back
struct edges{int to,next,dist;}edge[maxn*];
int l,next[maxn];
inline void addedge(int x,int y,int z) {
edge[++l]=(edges){y,next[x],z};next[x]=l;
edge[++l]=(edges){x,next[y],z};next[y]=l;
}
bool done[maxn];
int s[maxn],f[maxn],root,size;
void dfs(int u,int fa){
s[u]=;f[u]=;
int v=;
for (int i=next[u];i;i=edge[i].next) {
if (done[v=edge[i].to]||v==fa) continue;
dfs(v,u);
s[u]+=s[v];
f[u]=max(f[u],s[v]);
}
f[u]=max(f[u],size-s[u]);
if (f[u]<f[root]) root=u;
}
vector<iii> pre[maxn];
vector<ii> date[maxn][];
int a[maxn];
void getdist(int u,int fa,int tar,int dist,int cnt) {
pre[u].pb(iii(tar,ii(dist,cnt)));
date[tar][cnt].pb(ii(a[u],dist));
s[u]=;int v=;
for (int i=next[u];i;i=edge[i].next) {
if (done[v=edge[i].to]||v==fa) continue;
getdist(v,u,tar,dist+edge[i].dist,cnt);
s[u]+=s[v];
}
}
void work(int u){
done[u]=;
int v=;
pre[u].pb(iii(u,ii(,)));
int cnt=;
for (int i=next[u];i;i=edge[i].next) {
if (done[v=edge[i].to]) continue;
getdist(v,,u,edge[i].dist,cnt);
f[]=size=s[v];
dfs(v,root=);
work(root);
cnt++;
}
}
ll query(vector<ii> *a,ii date,int l,int r) {
ll ans=;
for (int i=;i<=;i++) {
if (i==date.se) continue;
int L=lower_bound(a[i].begin(),a[i].end(),ii(l,-))-a[i].begin(),
R=lower_bound(a[i].begin(),a[i].end(),ii(r+,-))-a[i].begin();
if (L) ans-=date.fi*L+a[i][L-].se;
if (R) ans+=date.fi*R+a[i][R-].se;
}
return ans;
}
int main(){
freopen("shop.in","r",stdin);
freopen("shop.out","w",stdout);
int n,Q,_;
scanf("%d%d%d",&n,&Q,&_);
for (int i=;i<=n;i++) scanf("%d",a+i);
for (int i=;i<n;i++) {
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z);
}
f[]=size=n;
dfs(,root=);
work(root);
for (int i=;i<=n;i++)
for (int j=;j<;j++) {
sort(date[i][j].begin(),date[i][j].end());
for (int k=;k<date[i][j].size();k++)
date[i][j][k].se+=date[i][j][k-].se;
}
ll ans=;
while (Q--) {
int u,x,y;
scanf("%d%d%d",&u,&x,&y);
int l=min((ans+x)%_,(ans+y)%_),r=max((ans+x)%_,(ans+y)%_);
ans=;
for (int i=;i<pre[u].size();i++) {
if (l<=a[pre[u][i].fi]&&a[pre[u][i].fi]<=r) ans+=pre[u][i].se.fi;
ans+=query(date[pre[u][i].fi],pre[u][i].se,l,r);
}
printf("%lld\n",ans);
}
return ;
}

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:

 #include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define maxn 110
#define mod 1000000007
vector<int> e[maxn];
#define pb push_back
int fa[maxn];
int find(int x) {return fa[x]==x?fa[x]:fa[x]=find(fa[x]);}
int c[maxn][maxn];
inline void init(){
for (int i=;i<=;i++) {
c[i][]=;
for (int j=;j<=i;j++) c[i][j]=(c[i-][j-]+c[i-][j])%mod;
}
}
struct info{
int level,f[maxn];
info(){level=;memset(f,,sizeof(f));}
inline int sum(){
int ans=;
for (int i=;i<=level;i++) (ans+=f[i])%=mod;
return ans;
}
};
inline info update(info x,info y) {
info ans;
for (int i=;i<=x.level;i++) {
for (int j=;j<=y.level;j++) {
for (int k=max(i,j);k<=i+j;k++) {
(ans.f[k]+=c[k][i]*1ll*c[i][i+j-k]%mod*x.f[i]%mod*y.f[j]%mod)%=mod;
}
}
}
ans.level=x.level+y.level;
return ans;
}
bool b[maxn];
info dfs(int x) {
info ans;
b[x]=;
for (int i=;i<e[x].size();i++)
if (i==) ans=dfs(e[x][i]);
else ans=update(ans,dfs(e[x][i]));
for (int i=ans.level;i;i--) ans.f[i+]=ans.f[i];
ans.f[]=ans.level==;
ans.level++;
return ans;
} int main(){
freopen("pairwise.in","r",stdin);
freopen("pairwise.out","w",stdout);
int n,m;
init();
scanf("%d%d",&n,&m);
for (int i=;i<=n;i++) fa[i]=i;
static int from[maxn],to[maxn];
static char opt[maxn][];
for (int i=;i<=m;i++) {
scanf("%d%s%d",from+i,opt[i],to+i);
if (opt[i][]=='=') fa[find(from[i])]=find(to[i]);
}
static int id[maxn],in[maxn];
for (int i=;i<=n;i++) id[i]=find(i);
for (int i=;i<=m;i++)
if (opt[i][]=='<') {
e[id[from[i]]].pb(id[to[i]]);
in[id[to[i]]]++;
}
info ans;
for (int i=;i<=n;i++){
if (!b[id[i]]&&in[id[i]]==) ans=ans.level==?dfs(id[i]):update(ans,dfs(id[i]));
}
for (int i=;i<=n;i++)
if (!b[id[i]]) {printf("%d\n",);return ;}
printf("%d\n",ans.sum());
return ;
}

HNOI2015 Day 2题解的更多相关文章

  1. [HNOI2015]菜肴制作 题解(自带口胡证明)

    [HNOI2015]菜肴制作 时间限制: 1 Sec  内存限制: 512 MB 题目描述 知名美食家小 A被邀请至ATM 大酒店,为其品评菜肴. ATM 酒店为小 A 准备了 N 道菜肴,酒店按照为 ...

  2. 洛谷 P3239 / loj 2112 [HNOI2015] 亚瑟王 题解【期望】【DP】

    ???看不懂的期望DP 题目描述 小 K 不慎被 LL 邪教洗脑了,洗脑程度深到他甚至想要从亚瑟王邪教中脱坑. 他决定,在脱坑之前,最后再来打一盘亚瑟王.既然是最后一战,就一定要打得漂亮.众所周知,亚 ...

  3. BZOJ4009 & 洛谷3242 & LOJ2113:[HNOI2015]接水果——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=4009 https://www.luogu.org/problemnew/show/P3242 ht ...

  4. 洛谷 P3243 [HNOI2015]菜肴制作 题解

    每日一题 day60 打卡 Analysis 这道题一看就感觉是个拓扑排序,但因为按字典序最小的排序会有问题(见第三个样例)主要原因是每次选择有后效性,而从后往前就不会存在这个问题,因为每个子任务都是 ...

  5. [HNOI2015]开店 简要题解

    主席树. 推下式子,发现点的深度和好算,lca深度和不好算. lca深度之和有个套路:先给a到根路径+1,再算b到根的和. 如果可以离线,即LNOI的LCA.本题强制在线,可持久化. 由于区间修改,使 ...

  6. [HNOI2015]菜肴制作 题解(贪心+拓扑)

    Description 知名美食家小 A被邀请至ATM 大酒店,为其品评菜肴. ATM 酒店为小 A 准备了 N 道菜肴,酒店按照为菜肴预估的质量从高到低给予 1到N的顺序编号,预估质量最高的菜肴编号 ...

  7. HNOI2015题解

    奇了怪了我上次发的题解怎么不见了? 题意自己戳链接-- Day 1 id=4008">HNOI2015 Arthur 思路:期望DP 直接DP是死也D不出的 转化一下 令f[i][j] ...

  8. [HNOI2015]部分题解

    Day1 T2 [HNOI2015]接水果 风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果.由于她已经DT FC 了The big black,  她觉得这个游戏太简单了, ...

  9. 【题解】[HNOI2015]菜肴制作(贪心+topo序)

    [题解][HNOI2015]菜肴制作(贪心+topo序) 题意:请你构造一个排列\(p[i]\)使得对于数组\(arc[i]=p[i]\)的字典序最小,并且对于给定的有序数对\((u,v)\)保证你给 ...

随机推荐

  1. RecyclerView学习笔记

    主要方法: setLayoutManager:设置显示方式,有LinearLayoutManager(像ListView一样的纵向列表布局),有GridLayoutManager(网格布局),有Sta ...

  2. jsoup的介绍使用(转)

    jsoup文档: http://www.open-open.com/jsoup/ 原链接: http://www.oschina.net/question/12_14127 jsoup 简介 Java ...

  3. BZOJ2064: 分裂

    2064: 分裂 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 360  Solved: 220[Submit][Status][Discuss] De ...

  4. SQLite高级:一库建多表,封装类

    package eoe.database; import android.content.Context; import android.database.sqlite.SQLiteDatabase; ...

  5. iOS 之 网络 NSURLSession

    NSURLSession相比NSURLConnection功能更强大,是更上层的网络封装.相比而言,普通应用场景下NSURLSession没有什么优势,但是,在程序切换到后台的情况下,NSURLSes ...

  6. systemd的命令systemctl set-property testSpeed CPUQuota=10%

    总结 systemd 的资源限制一般要写到unit文件中,但是,现在测试发现会有 被值被覆盖的现象:经过排查发现是,没有 使用systemd的接口,凡是使用echo "" > ...

  7. C#多线程-volatile、lock关键字

    volatile是C#中最简单的一种同步关键字,其意义是针对程序中一些敏感数据,不允许多线程同时访问,保证数据在任何访问时刻,最多有一个线程访问,以保证数据的完整性,虽与java中的synchroni ...

  8. SQL查询今天、昨天、7天内、30天

    今天的所有数据:select * from 表名 where DateDiff(dd,datetime类型字段,getdate())=0 昨天的所有数据:select * from 表名 where ...

  9. JWPlayer使用指南

    http://support.jwplayer.com/customer/portal/articles/1499103-quickstart-reference <div id="m ...

  10. Flex Socket 安全沙箱问题解决

    Flex使用Socket与C++通讯时遇到了安全沙箱问题,NND,折腾我半天,这是我的解决方法: 1):策略文件与主套接字在同一端口,只需调用 Socket.connect() 或 XMLSocket ...