「ZJOI2016」解题报告
「ZJOI2016」解题报告
我大浙的省选题真是超级神仙……这套已经算是比较可做的了。
「ZJOI2016」旅行者
神仙分治题。
对于一个矩形,每次我们从最长边切开,最短边不会超过 \(\sqrt{n\times m}\),所以对于每个点跑一遍最短路就行了。
时间复杂度 \(O(n\sqrt{n}\log n+q\sqrt{n})\)
\(Code\ Below:\)
#include <bits/stdc++.h>
#define id(i,j) (((i)-1)*m+(j))
using namespace std;
const int maxn=20000+10;
const int maxm=100000+10;
const int inf=0x3f3f3f3f;
int n,m,Q,val[maxn][4],dis[maxn],vis[maxn],ans[maxm];
int nx[4]={0,1,0,-1},ny[4]={1,0,-1,0};
struct node{
int x,y,dis;
node(int x=0,int y=0,int dis=0):x(x),y(y),dis(dis){}
};
inline bool operator < (const node &a,const node &b){
return a.dis>b.dis;
}
struct Query{
int x1,x2,y1,y2,id;
}q[maxm],q1[maxm],q2[maxm];
inline int read(){
register int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return (f==1)?x:-x;
}
inline void Dijkstra(int sx,int sy,int lx,int ly,int rx,int ry){
priority_queue<node> pq;
for(int i=lx;i<=rx;i++)
for(int j=ly;j<=ry;j++) dis[id(i,j)]=inf,vis[id(i,j)]=0;
dis[id(sx,sy)]=0;pq.push(node(sx,sy,0));
node u;int x,y;
while(!pq.empty()){
u=pq.top();pq.pop();
if(vis[id(u.x,u.y)]) continue;
vis[id(u.x,u.y)]=1;
for(int i=0;i<4;i++){
x=u.x+nx[i];y=u.y+ny[i];
if(x<lx||x>rx||y<ly||y>ry) continue;
if(dis[id(x,y)]>dis[id(u.x,u.y)]+val[id(u.x,u.y)][i]){
dis[id(x,y)]=dis[id(u.x,u.y)]+val[id(u.x,u.y)][i];
pq.push(node(x,y,dis[id(x,y)]));
}
}
}
}
void solve(int lx,int ly,int rx,int ry,int ql,int qr){
if(ql>qr) return ;
if(lx==rx&&ly==ry){
for(int i=ql;i<=qr;i++) ans[q[i].id]=0;
return ;
}
if(rx-lx>ry-ly){
int mid=(lx+rx)>>1,cnt1=0,cnt2=0;
for(int i=ly;i<=ry;i++){
Dijkstra(mid,i,lx,ly,rx,ry);
for(int j=ql;j<=qr;j++) ans[q[j].id]=min(ans[q[j].id],dis[id(q[j].x1,q[j].y1)]+dis[id(q[j].x2,q[j].y2)]);
}
for(int i=ql;i<=qr;i++){
if(q[i].x1<=mid&&q[i].x2<=mid) q1[++cnt1]=q[i];
if(q[i].x1>mid&&q[i].x2>mid) q2[++cnt2]=q[i];
}
for(int i=1;i<=cnt1;i++) q[ql+i-1]=q1[i];
for(int i=1;i<=cnt2;i++) q[ql+cnt1+i-1]=q2[i];
solve(lx,ly,mid,ry,ql,ql+cnt1-1);
solve(mid+1,ly,rx,ry,ql+cnt1,ql+cnt1+cnt2-1);
}
else {
int mid=(ly+ry)>>1,cnt1=0,cnt2=0;
for(int i=lx;i<=rx;i++){
Dijkstra(i,mid,lx,ly,rx,ry);
for(int j=ql;j<=qr;j++) ans[q[j].id]=min(ans[q[j].id],dis[id(q[j].x1,q[j].y1)]+dis[id(q[j].x2,q[j].y2)]);
}
for(int i=ql;i<=qr;i++){
if(q[i].y1<=mid&&q[i].y2<=mid) q1[++cnt1]=q[i];
if(q[i].y1>mid&&q[i].y2>mid) q2[++cnt2]=q[i];
}
for(int i=1;i<=cnt1;i++) q[ql+i-1]=q1[i];
for(int i=1;i<=cnt2;i++) q[ql+cnt1+i-1]=q2[i];
solve(lx,ly,rx,mid,ql,ql+cnt1-1);
solve(lx,mid+1,rx,ry,ql+cnt1,ql+cnt1+cnt2-1);
}
}
int main()
{
memset(val,inf,sizeof(val));
memset(ans,inf,sizeof(ans));
n=read(),m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<m;j++) val[id(i,j)][0]=val[id(i,j+1)][2]=read();
for(int i=1;i<n;i++)
for(int j=1;j<=m;j++) val[id(i,j)][1]=val[id(i+1,j)][3]=read();
Q=read();
for(int i=1;i<=Q;i++) q[i].x1=read(),q[i].y1=read(),q[i].x2=read(),q[i].y2=read(),q[i].id=i;
solve(1,1,n,m,1,Q);
for(int i=1;i<=Q;i++) printf("%d\n",ans[i]);
return 0;
}
「ZJOI2016」小星星
树形 \(dp\) + 容斥原理。
第一次见到对于排列容斥的题,大开眼界。(可能是我太弱)
那么 \(O(n^3)\) 的树形 \(dp\) 还是好打的嘛。
\(Code\ Below:\)
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n,m,e[20][20],sta[20],top;
vector<int> G[20];ll dp[20][20],ans;
void treedp(int x,int f){
vector<int>::iterator it;
int y;ll now;
for(it=G[x].begin();it!=G[x].end();it++)
if(*it!=f) treedp(*it,x);
for(int i=1;i<=top;i++){
dp[x][i]=1;
for(it=G[x].begin();it!=G[x].end();it++){
y=*it;
if(y==f) continue;
now=0;
for(int j=1;j<=top;j++)
if(e[sta[i]][sta[j]]) now+=dp[y][j];
dp[x][i]*=now;
}
}
}
inline void solve(){
treedp(1,0);
for(int i=1;i<=top;i++){
if((n-top)&1) ans-=dp[1][i];
else ans+=dp[1][i];
}
}
void dfs(int x){
if(x==n+1){solve();return;}
sta[++top]=x;dfs(x+1);
top--;dfs(x+1);
}
int main()
{
scanf("%d%d",&n,&m);
int x,y;
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
e[x][y]=e[y][x]=1;
}
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
dfs(1);
printf("%lld\n",ans);
return 0;
}
「ZJOI2016」线段树
神仙 \(dp\) 系列。
\(f[x][p][l][r]\) 表示经过 \(p\) 轮操作后 \(\text{max}_{i=l}^{r}{a_i}\leq x<\text{min}(a_{l-1},a_{r+1})\) 的方案数。
那么 \(x\) 可以枚举省掉一维,\(p\) 可以滚动数组,那么实际只有两位。我们先看 \(O(n^4)\) 的解法。
\(f[p][l][r]\) 可以由三项转移:
区间左右端点在 \([1,l-1]\) 或 \([l,r]\) 或 \([r+1,n]\):\(f[p][l][r]=[{l-1\choose 2}+{r-l+1\choose 2}+{n-r\choose 2}]\times f[p-1][l][r]\)
区间右端点在 \(l-1\):\(f[p][l][r]=\sum_{i=1}^{l-1}(i-1)\times f[p-1][i][r]\)
区间左端点在 \(r+1\):\(f[p][l][r]=\sum_{i=r+1}^{n}(n-i)\times f[p-1][l][i]\)
现在看 \(O(n^3)\) 的解法。
不容易发现可以把 \(x\) 一起转移,然后就 \(O(n^3)\) 的解法了。
代码是 \(O(n^4)\) 的。
\(Code\ Below:\)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=400+10;
const int mod=1e9+7;
int n,m,a[maxn],mp[maxn],cnt[maxn],C[maxn][maxn],sum[maxn][maxn],f[2][maxn][maxn],g[maxn][maxn];
bool is[maxn][maxn];
/*
cnt[i] = i * (i + 1) / 2
f(i, l, r) += f(i - 1, l, r) * (cnt[r - l + 1] + cnt[n - r] + cnt[l - 1]);
f(i, l, r) += \sum _ {u = 1} ^ {l - 1} f(i - 1, u, r) * (u - 1)
f(i, l, r) += \sum _ {v = r + 1} ^ {n} f(i - 1, l, v) * (n - v)
*/
inline void solve(int x,int L,int R){
memset(f[0],0,sizeof(f[0]));
for(int i=L;i<=R;i++) is[i][a[x]]=1;
f[0][L][R]=1;
int now,lst,val;
for(int i=1;i<=m;i++){
now=i&1;lst=i&1^1;
for(int l=L;l<=R;l++)
for(int r=l;r<=R;r++) f[now][l][r]=f[lst][l][r]*C[l][r]%mod;
for(int r=L;r<=R;r++){
val=0;
for(int l=L;l<=r;l++){
(f[now][l][r]+=val)>=mod?f[now][l][r]-=mod:0;
val=(val+f[lst][l][r]*(l-1))%mod;
}
}
for(int l=L;l<=R;l++){
val=0;
for(int r=R;r>=l;r--){
(f[now][l][r]+=val)>=mod?f[now][l][r]-=mod:0;
val=(val+f[lst][l][r]*(n-r))%mod;
}
}
}
//[L, i] [i, R]
for(int i=L-1;i<=R;i++)
for(int j=L-1;j<=R;j++) sum[i][j]=0;
for(int i=L;i<=R;i++)
for(int j=L;j<=R;j++) sum[i][j]=(sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+f[m&1][i][j])%mod;
for(int i=L;i<=R;i++) g[i][a[x]]=((g[i][a[x]]+sum[i][R]-sum[L-1][R]-sum[i][i-1]+sum[L-1][i-1])%mod+mod)%mod;
}
signed main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),mp[i]=a[i];
sort(mp+1,mp+n+1);
for(int i=1;i<=n;i++) a[i]=lower_bound(mp+1,mp+n+1,a[i])-mp;
for(int i=1;i<=n;i++) cnt[i]=1ll*i*(i+1)/2%mod;
for(int l=1;l<=n;l++)
for(int r=l;r<=n;r++) C[l][r]=(cnt[l-1]+cnt[n-r]+cnt[r-l+1])%mod;
int L,R;
for(int i=1;i<=n;i++){
L=i;R=i;
while(L>1&&a[L-1]<a[i]) L--;
while(R<n&&a[i]>=a[R+1]) R++;
solve(i,L,R);
}
int tmp,val,ans;
for(int i=1;i<=n;i++){
val=0;
for(int j=1;j<=n;j++){
if(!is[i][j]) continue;
tmp=g[i][j];g[i][j]-=val;
if(g[i][j]<0) g[i][j]+=mod;
val=tmp;
}
}
for(int i=1;i<=n;i++){
ans=0;
for(int j=1;j<=n;j++) ans=(ans+g[i][j]*mp[j])%mod;
printf("%lld ",ans);
}
printf("\n");
return 0;
}
「ZJOI2016」大森林
「ZJOI2016」解题报告的更多相关文章
- 「ZJOI2016」大森林 解题报告
「ZJOI2016」大森林 神仙题... 很显然线段树搞不了 考虑离线操作 我们只搞一颗树,从位置1一直往后移动,然后维护它的形态试试 显然操作0,1都可以拆成差分的形式,就是加入和删除 因为保证了操 ...
- 「ZJOI2016」旅行者 解题报告
「ZJOI2016」旅行者 对网格图进行分治. 每次从中间选一列,然后枚举每个这一列的格子作为起点跑最短路,进入子矩形时把询问划分一下,有点类似整体二分 至于复杂度么,我不会阿 Code: #incl ...
- 2090. 「ZJOI2016」旅行者 分治,最短路
2090. 「ZJOI2016」旅行者 链接 loj 思路 \((l,mid)(mid+1,r)\).考虑跨过mid的贡献. 假设选的中间那条线的点为gzy,贡献为\(dis(x,gzy)+dis(g ...
- 「雅礼集训 2017 Day2」解题报告
「雅礼集训 2017 Day2」水箱 我怎么知道这种题目都能构造树形结构. 根据高度构造一棵树,在树上倍增找到最大的小于约束条件高度的隔板,开一个 \(vector\) 记录一下,然后对于每个 \(v ...
- 「雅礼集训 2017 Day1」 解题报告
「雅礼集训 2017 Day1」市场 挺神仙的一题.涉及区间加.区间除.区间最小值和区间和.虽然标算就是暴力,但是复杂度是有保证的. 我们知道如果线段树上的一个结点,\(max=min\) 或者 \( ...
- 「JOI2019 Final」解题报告
传送门 「JOI2019 Final」勇者比太郎 看懂题就很简单了,后缀和随便维护一下就好了,别用树状数组强加一个\(\log\)就行. 「JOI2019 Final」画展 显然可以先把所有的画框按大 ...
- 「THP3考前信心赛」解题报告
目录 写在前面&总结: T1 T2 T3 T4 写在前面&总结: \(LuckyBlock\) 良心出题人!暴力分给了 \(120pts\) \(T1\) 貌似是个结论题,最后知道怎么 ...
- 洛谷 P3349 [ZJOI2016]小星星 解题报告
P3349 [ZJOI2016]小星星 题目描述 小\(Y\)是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品.她有\(n\)颗小星星,用\(m\)条彩色的细线串了起来,每条细线连着两颗小星星. 有一 ...
- 【LOJ】#2090. 「ZJOI2016」旅行者
题解 每次按较长边把矩形分成两半,找一个中间轴,轴上的每个点跑一边最短路更新所有的答案 然后把矩形分成两半,递归下去 代码 #include <bits/stdc++.h> #define ...
随机推荐
- redis各类错误可能的解决方案
.报Timeout performing EVAL 这个可能是连到读库从库了,直接改成写库就没问题了. 2. 各种ConnectTimeout 一般是偶尔或经常就超时,这种情况,找了各种回答,最后在s ...
- tiny4412--linux驱动学习(2)
在ubuntu下编写验证字符设备驱动 1,准备工作 uname -r 查看电脑版本信息 apt-get install linux-source 安装相应版本的linux内核 2,编写驱动程序 ...
- python 导出数据到excel 中,一个好用的导出数据到excel模块,XlsxWriter
最近公司有项目需要导出数据到excel,首先想到了,tablib,xlwt,xlrd,xlwings,win32com[还可以操作word],openpyxl,等模块但是 实际操作中tablib 写入 ...
- vba多条件查询更新EXCEL数据导access数据库
功能:根据项目号和步骤ID,更新指定步骤完成时间.这里里边要匹配两个条件一个是项目ID “”projectID“” 另一个是 “”StepID“” 步骤ID. Sub SaveFini() Dim r ...
- Python中安装bs4后,pycharm依然报错ModuleNotFoundError: No module named 'bs4'
学习网络抓取时,第一步出现问题. 执行示例代码 from urllib.request import urlopen from bs4 import BeautifulSoup html = urlo ...
- appium定位toast消息的使用
定位使用xpath后,定位消息文本,然后使用text获取消息文本做断言.toast_loc = ("xpath", ".//*[contains(@text,'切换运营商 ...
- 创建服务的注册与发现 Eureka (四)
一.eureka注册中心 1.创建一个工程 工程名:microservicecloud-eureka-7001 2.向pom文件中增加如下: <dependencies> <!--e ...
- python生成器 获取 目录下文件
# os.walk()和os.list 都是得到所有文件的列表, 如果目录下文件特别多, 上亿了, 我们就需要生成器的方式获取 # 要求目录下面没有目录, 会递归到子目录下面找文件, (如果有子目录可 ...
- Linux内核基本装载卸载
Linux由两部分组成:内核核心+内核模块核心的命名通常是vmlinuz-VERSION-release,模块的放置于/lib/modules/VERSION-release/ 一旦系统运行起来,内核 ...
- 使用Mobile Device Manager Plus mdm软件进行完备的移动设备管理
使用Mobile Device Manager Plus mdm软件进行完备的移动设备管理 什么是移动设备管理(mdm管理系统)? 移动设备管理(mdm管理系统)旨在管理企业内移动设备.管理员使用md ...