LOJ 3056 「HNOI2019」多边形——模型转化+树形DP
题目:https://loj.ac/problem/3056
只会写暴搜。用哈希记忆化之类的。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define ll long long
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
const int N=,M=1e6+,bs=,md=1e9+,mod=1e9+,INF=M;
int upt(int x){while(x>=mod)x-=mod;while(x<)x+=mod;return x;} int W,n,lm,tot;
struct Dt{
int x,y;
Dt(int x=,int y=):x(x),y(y) {}
bool operator< (const Dt &b)const
{return x==b.x?y<b.y:x<b.x;}
bool operator== (const Dt &b)const
{return x==b.x&&y==b.y;}
}c[M];
struct Node{
Dt a[N];
void sort(){std::sort(a+,a+lm+);}
int hs()
{
int ret=;
for(int i=;i<=lm;i++)
{
ret=((ll)ret*bs+a[i].x)%md;
ret=((ll)ret*bs+a[i].y)%md;
}
return ret;
}
}s[N];
map<int,int> mp;
Node cz(Node u,Dt k)
{
int a=k.x,b=,c=k.y,d=;
for(int i=;i<=lm;i++)
{
Dt tp=u.a[i]; if(tp.x>a)break;
if(tp.x==a&&tp.y<c)b=tp.y;
if(tp.x==a&&tp.y>c&&!d)d=tp.y;
}
if(!b) b=a+;
if(!d)
{
int ty=;
for(int i=;i<=lm;i++)
if(u.a[i].x==a){ty=u.a[i].y;break;}
if(ty<a) d=ty; else d=(a==?n:a-);//ty<a!!!
}
if(b>d){u.a[].x=;return u;}///
for(int i=;i<=lm;i++)
{
Dt tp=u.a[i]; if(tp.x!=a||tp.y!=c)continue;
u.a[i].x=b; u.a[i].y=d;
for(int j=i+;j<=lm;j++)
if(u.a[j]<u.a[j-])swap(u.a[j],u.a[j-]);
for(int j=i-;j;j--)
if(u.a[j+]<u.a[j])swap(u.a[j],u.a[j+]);
}
return u;
}
Dt dfs(Node cr)
{
int h=cr.hs(); if(mp.count(h))return c[mp[h]];
Dt lj=Dt(INF,);
for(int i=;i<=lm;i++)
{
if(cr.a[i].y==n)continue;
Node to=cz(cr,cr.a[i]); if(!to.a[].x)continue;
Dt d=dfs(cz(cr,cr.a[i])); d.x++;
if(d.x==lj.x)lj.y=upt(lj.y+d.y);
else if(d.x<lj.x)lj=d;
}
if(lj.x==INF)lj=Dt(,);
mp[h]=++tot; return c[tot]=lj;
}
void solve()
{
lm=n-;
for(int i=,u,v;i<=lm;i++)
{
u=rdn(); v=rdn(); if(u>v)swap(u,v);
s[].a[i]=Dt(u,v);
}
s[].sort();
int m=rdn();
for(int i=,u,v;i<=m;i++)
{
u=rdn();v=rdn(); if(u>v)swap(u,v);
s[i]=cz(s[],Dt(u,v));
}
dfs(s[]);
for(int i=;i<=m;i++)
{
Dt d=c[mp[s[i].hs()]];
if(!W)printf("%d\n",d.x);
else printf("%d %d\n",d.x,d.y);
}
}
int main()
{
W=rdn(); n=rdn();
if(n<=)solve();
return ;
}
应该更大胆一些。果然就是最终每条边都与 n 点相连、每次能把一条边变成这样。
那么第一问的答案就是 ( n-3 ) - ( 初始就与 n 相连的边数 ) 。
并且这样的话,可以看出一个二叉树森林。就是把一条边旋转成与 n 相连之后,分出两个部分,两个部分里的边在该边旋转之后才能旋转。
设这条边的左孩子有 a 步、右孩子有 b 步,自己的方案就是 \( dp[ ls ] * dp[ rs ] * \binom{a+b}{a} \) 。 m=0 的这样做一下就行了。
一开始旋转一条边是 rotate 操作。(从“出现的边是什么”的角度来看,确实是 rotate)
如果旋转的是某个二叉树的根,就是令答案步数 -1 , 把该二叉树从根断成两个二叉树。
如果旋转的不是根,发现对上面的影响只有 \( dp[ ] \) 的改变,所以除掉原来的再乘上现在的。
如果旋转的是根,不仅 \( dp[ ] \) 变了,一些 \( \binom{a+b}{a} \) 也变了,所以不能除掉再乘。维护前缀和后缀答案即可。
把边按 “左端点递增、右端点递减” (左端点指标号小的点)排序,找森林结构的时候,对于边的区间 [ L , R ] , L 是第一个要旋转的,然后左端点在 L 的右端点之前(严格)的边是自己的左孩子,其他是右孩子。找分界的时候自己用了 lower_bound ,反正每次剥掉一个 L ,一共调用 n 次 lower_bound ,而且调用的数组大小还会变小,所以复杂度还可以。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
const int N=1e5+,mod=1e9+;
int pw(int x,int k)
{int ret=;while(k){if(k&)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=;}return ret;} int n,fa[N],c[N][],sta[N],top,rk[N],jc[N],jcn[N],ans;
struct Node{
int x,y;
Node(int x=,int y=):x(x),y(y) {}
bool operator< (const Node &b)const
{return x==b.x?y>b.y:x<b.x;}
}ed[N],dp[N],pr[N],sc[N];
int C(int n,int m){return (ll)jc[n]*jcn[m]%mod*jcn[n-m]%mod;}
Node mrg(Node u,Node v)
{ u.y+=v.y; u.x=(ll)u.x*v.x%mod*C(u.y,v.y)%mod; return u;}
void pshp(int cr)
{
dp[cr]=Node(,);
if(c[cr][])dp[cr]=mrg(dp[cr],dp[c[cr][]]);
if(c[cr][])dp[cr]=mrg(dp[cr],dp[c[cr][]]);
dp[cr].y+=(cr!=);
}
void ini_dfs(int L,int R)
{
if(L==R){pshp(L);return;}
if(ed[L+].x!=ed[L].x)
{
ini_dfs(L+,R);
c[L][]=L+; fa[L+]=L; pshp(L); return;
}
Node d=Node(ed[L+].y,n+);
int mid=lower_bound(ed+L+,ed+R+,d)-ed;
ini_dfs(L+,mid-); c[L][]=L+; fa[L+]=L;
if(mid<=R){ ini_dfs(mid,R); c[L][]=mid; fa[mid]=L;}
pshp(L);
}
void init()
{
jc[]=;for(int i=;i<=n;i++)jc[i]=(ll)jc[i-]*i%mod;
jcn[n]=pw(jc[n],mod-);
for(int i=n-;i>=;i--)jcn[i]=(ll)jcn[i+]*(i+)%mod;//before!!! int lm=n-;
sort(ed+,ed+lm+);
for(int i=,lst=;i<=lm;i++)
{
while(i<=lm&&ed[i].y!=n)i++;
if(lst<i)
{
ini_dfs(lst,i-);
sta[++top]=lst; rk[lst]=top;
pr[top]=mrg(pr[top-],dp[lst]);
}
if(i<=lm)///way:1, siz:0
{
sta[++top]=i; rk[i]=top;
pr[top]=mrg(pr[top-],dp[i]);
}
lst=i+;
}
for(int i=top;i;i--)sc[i]=mrg(sc[i+],dp[sta[i]]);
}
int rotate(int x)
{
int y=fa[x],d=(x==c[y][]);
Node nx=dp[x], ny=dp[y]; c[y][d]=c[x][!d]; c[x][!d]=y;
pshp(y); pshp(x);
int ret=(ll)dp[].x*pw(ny.x,mod-)%mod*dp[x].x%mod;
c[x][!d]=c[y][d]; c[y][d]=x; dp[x]=nx; dp[y]=ny;
return ret;
}
int main()
{
int W=rdn(); n=rdn(); int lm=n-;
for(int i=,u,v;i<=lm;i++)
{ u=rdn();v=rdn();if(u>v)swap(u,v);ed[i]=Node(u,v);}
ans=lm;
for(int i=;i<=lm;i++)if(ed[i].y==n)ans--;
init(); dp[]=pr[top];
if(!W)printf("%d\n",ans);
else printf("%d %d\n",ans,dp[].x);
int m=rdn();
for(int i=,u,v,k;i<=m;i++)
{
u=rdn();v=rdn();if(u>v)swap(u,v);
k=lower_bound(ed+,ed+lm+,Node(u,v))-ed;
if(!fa[k])
{
Node d=mrg(pr[rk[k]-],sc[rk[k]+]);
if(c[k][])d=mrg(d,dp[c[k][]]);
if(c[k][])d=mrg(d,dp[c[k][]]);
if(!W)printf("%d\n",ans-);
else printf("%d %d\n",ans-,d.x);
}
else
{
if(!W)printf("%d\n",ans);
else printf("%d %d\n",ans,rotate(k));
}
}
return ;
}
LOJ 3056 「HNOI2019」多边形——模型转化+树形DP的更多相关文章
- Loj #3056. 「HNOI2019」多边形
Loj #3056. 「HNOI2019」多边形 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时针方向标号依次为 \(1,2,3, \ldots , n\).最开 ...
- LOJ 2719 「NOI2018」冒泡排序——模型转化
题目:https://loj.ac/problem/2719 首先要发现合法的充要条件是 | LDS | <=2 ! 因为有没用的步数,说明一个元素先往左移.又往右移(不会先往右移再往左移,因为 ...
- Loj #3059. 「HNOI2019」序列
Loj #3059. 「HNOI2019」序列 给定一个长度为 \(n\) 的序列 \(A_1, \ldots , A_n\),以及 \(m\) 个操作,每个操作将一个 \(A_i\) 修改为 \(k ...
- Loj #3055. 「HNOI2019」JOJO
Loj #3055. 「HNOI2019」JOJO JOJO 的奇幻冒险是一部非常火的漫画.漫画中的男主角经常喜欢连续喊很多的「欧拉」或者「木大」. 为了防止字太多挡住漫画内容,现在打算在新的漫画中用 ...
- Loj 3058. 「HNOI2019」白兔之舞
Loj 3058. 「HNOI2019」白兔之舞 题目描述 有一张顶点数为 \((L+1)\times n\) 的有向图.这张图的每个顶点由一个二元组 \((u,v)\) 表示 \((0\le u\l ...
- Loj #3057. 「HNOI2019」校园旅行
Loj #3057. 「HNOI2019」校园旅行 某学校的每个建筑都有一个独特的编号.一天你在校园里无聊,决定在校园内随意地漫步. 你已经在校园里呆过一段时间,对校园内每个建筑的编号非常熟悉,于是你 ...
- 【loj - 3056】 「HNOI2019」多边形
目录 description solution accepted code details description 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时 ...
- LOJ 3057 「HNOI2019」校园旅行——BFS+图等价转化
题目:https://loj.ac/problem/3057 想令 b[ i ][ j ] 表示两点是否可行,从可行的点对扩展.但不知道顺序,所以写了卡时间做数次 m2 迭代的算法,就是每次遍历所有不 ...
- LOJ 3059 「HNOI2019」序列——贪心与前后缀的思路+线段树上二分
题目:https://loj.ac/problem/3059 一段 A 选一个 B 的话, B 是这段 A 的平均值.因为 \( \sum (A_i-B)^2 = \sum A_i^2 - 2*B \ ...
随机推荐
- mysql的sql语句的性能诊断分析
1> explain SQL,类似于Oracle中explain语句 例如:explain select * from nad_aditem; 2> select benchmark(co ...
- git中工作区,缓存区,本地库,远程库的简要区别
git中工作区,缓存区,本地库,远程库的简要区别 实际上前三个名词对应到实体,都是你从远程仓库克隆下来的那个项目目录!只不过工作区就是你实际看到的目录和目录里的内容,当你修改/添加/删除了从远程仓库c ...
- Day4作业及默写
1,写代码,有如下列表,按照要求实现每一个功能 li = ["alex", "WuSir", "ritian", "barry&q ...
- git命令收集(记得持续更新)
这里收集了一些常用的git命令: git remote add origin git@192.168.1.128:sabo/ycdd-server.git git push -u origin mas ...
- 10.我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。 请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形. 请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? 是不是发现看不懂,哈哈:编程题就是这样,一定要归纳,手写过程: n ...
- Nginx配置之location模块和proxy模块
1.location指令的用法介绍 Location主要用来匹配url,如:http://www.beyond.com/nice,在这里对于location来说www.beyond.com是域名,/n ...
- Gym - 101806T: Touch The Sky(贪心)
Figure: The house floats up in the sky by balloons. This picture is also used in 2018 KAIST RUN Spri ...
- Gym .101879 USP Try-outs (寒假自训第七场)
B .Aesthetics in poetry 题意:给定N个数,(N<2000 ,a[i] <=1e9),让你找一个最大的K,使得N个数膜K的余数个数全都等于N/K个. 思路:我们找到N ...
- 20155219 2016-2017-2 《Java程序设计》第10周学习总结
20155219 2016-2017-2 <Java程序设计>第10周学习总结 教材学习内容总结 教材学习内容总结 Java的网络编程 网络编程 网络编程就是在两个或两个以上的设备(例如计 ...
- nginx安装,配置,并可以放静态文件教程
简单介绍一下:nginx是一个高性能的HTTP和反向代理的服务器.能有处理最高5,0000的并发量,是新兴的一个代理服务器.中国大陆使用nginx网站用户有:百度.京东.新浪.网易.腾讯.淘宝等. 安 ...