昨天做了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. 012-ViewState状态保持

    客户端的状态保持方案:ViewState.隐藏域.Cookies.控件状态.URL查询参数服务端的状态保持方案:Session(会话).Application.Caching(缓存).DataBase ...

  2. 非root用户Memcached repcached安装

    安装memcached前先要确定系统是否安装了gcc: 1.解压安装包: tar -zxf memcached-1.2.8-repcached-2.2.tar.gz 2.编译: 系统应安装了libev ...

  3. java域名解析

    DNS原理:http://amon.org/dns-introduction.html 根域:就是所谓的“.” 根域服务器只是具有13个IP地址,但机器数量却不是13台,因为这些IP地址借助了任播的技 ...

  4. Leetcode 181. Employees Earning More Than Their Managers

    The Employee table holds all employees including their managers. Every employee has an Id, and there ...

  5. IOS web app一些实用的属性设置

    IOS对safari私有的属性很多,虽然很多不为人知但是却很实用.掌握好这些属性对web app和混合app的开发会很有帮助. 1.format-detection[telephone=no] 是否自 ...

  6. Python3基础 global关键字 使函数的局部变量升格为全局变量

    镇场诗: 诚听如来语,顿舍世间名与利.愿做地藏徒,广演是经阎浮提. 愿尽吾所学,成就一良心博客.愿诸后来人,重现智慧清净体.-------------------------------------- ...

  7. Eclipse和debug的一些快捷键

    F5单步调试进入函数内部.  F6单步调试不进入函数内部,如果装了金山词霸2006则要把“取词开关”的快捷键改成其他的.  F7由函数内部返回到调用处.  F8一直执行到下一个断点.  F11 这个好 ...

  8. 完美解决夏天电脑cpu发烫问题

    最近有朋友跟我反馈,说苹果电脑虽然好用,但是一直有一个问题困扰着他,就是电脑散热的问题.每到夏天的时候,电脑运转之后就会发烫,用的特别的不舒服. 相信用电脑的都会有这样的感受吧,更加相信你们都用过以下 ...

  9. RadioGroup

    获取选中的Text radioGroup1.Properties.Items[radioGroup1.SelectedIndex].Description

  10. SQL,SP与ORM

    SQL译为按每一次情况的办理,SP意为存储过程,ORM就是对象-关系映射,比如Hibernate 一,演变  刚开始的时候,只有sql语句,即可以用交互模式一句一句执行, 也可以用批模式执行,多行sq ...