HNOI2018题解
在此处输入标题
标签(空格分隔): 未分类
重做了一遍,本来以为很快的,结果搞了一天。。。
寻宝游戏
可以发现只有\(\&0\)和\(|1\)会对答案有影响
那么对于每一位,我们只要知道最后一个\(\&1\)和最后一个\(|1\)谁近就可以了。
发现并不好做,我们可以把操作串也当成\(01\)串,如果\(\&=0,|=1\)好像并没有什么用,于是我们令\(\&=1,|=0\)发现这样刚好满足了我们需要的信息,设\(op\)为操作串,这一位串为\(a\),如果\(op\)字典序小于\(a\)最后会是\(1\),否则为\(0\)。(\(|1=01,\&0=10\)这就是字典序了,手玩也可以
那么我们把原串基数排序,那么一定可以重排成\(00...011...1\),否则无解。如果有解那答案就是第一个\(1\)串代表的十进数值减掉最后一个\(0\)串十进制数值。注意下边界条件。
\(code\)
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define gt getchar()
#define ll long long
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
typedef std::pair<int,int> P;
#define mk std::make_pair
#define fr first
#define sc second
inline int in()
{
int k=0;char ch=gt;bool p=1;
while(ch<'-')ch=gt;if(ch=='-')ch=gt,p=0;
while(ch>'-')k=k*10+ch-'0',ch=gt;
return p?k:-k;
}
const int YL=1e9+7,N=1005,M=5005;
inline int ksm(int a,int k){int r=1;while(k){if(k&1)r=1ll*r*a%YL;a=1ll*a*a%YL,k>>=1;}return r;}
inline int MO(const int &x){return x>=YL?x-YL:x;}
int pw[M],s[N][M],id[2][M],rk[M],res[M];
int main()
{
int n=in(),m=in(),q=in();
for(int i=1;i<=n;++i)
{
static char S[M];scanf("%s",S+1);
for(int j=1;j<=m;++j)s[i][j]=S[j]-'0';
}
for(int i=1;i<=m;++i)id[0][i]=i;
int now=0;pw[0]=1;
for(int i=1;i<=n;++i)
{
now^=1;int cnt=0,tot=0;pw[i]=MO(pw[i-1]<<1);
for(int j=1;j<=m;++j)cnt+=s[i][j]^1;
for(int j=1;j<=m;++j)
if(s[i][id[now^1][j]])id[now][++cnt]=id[now^1][j];
else id[now][++tot]=id[now^1][j];
}
int *p=id[now];
for(int i=1;i<=m;++i)rk[p[i]]=i;
for(int i=1;i<=m;++i)
for(int j=1;j<=n;++j)
res[i]=MO(res[i]+s[j][i]*pw[j-1]);
res[m+1]=pw[n];p[m+1]=m+1;
while(q--)
{
static char S[M];scanf("%s",S+1);
int mx=-1,mi=m+1;
for(int i=1;i<=m;++i)
if(S[i]=='0')mx=std::max(mx,rk[i]);
else mi=std::min(mi,rk[i]);
if(mx>=mi){puts("0");continue;}
printf("%d\n",MO(res[p[mi]]-res[p[mx]]+YL));
}
return 0;
}
转盘
可以发现,走一圈是最优的。
那么即求\(min(max(T_j-j)+i)+n-1\)。
楼房重建即可
\(code\)
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define gt getchar()
#define ll long long
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
typedef std::pair<int,int> P;
#define mk std::make_pair
#define fr first
#define sc second
inline int in()
{
int k=0;char ch=gt;bool p=1;
while(ch<'-')ch=gt;if(ch=='-')ch=gt,p=0;
while(ch>'-')k=k*10+ch-'0',ch=gt;
return p?k:-k;
}
const int N=2e5+5;
int mis[N<<2],mip[N<<2],mxv[N<<2],a[N];
#define lc k<<1
#define rc k<<1|1
#define ls l, mid ,lc
#define rs mid+1,r,rc
#define mid ((l+r)>>1)
int calc(int mh,int l,int r,int k)
{
if(l==r)return std::max(mh,a[l])+l;int w=mxv[rc];
if(mh>=w)return std::min(calc(mh,ls),mh+mid+1);
else return std::min(calc(mh,rs),mis[k]);
}
inline void up(int l,int r,int k)
{
mxv[k]=std::max(mxv[lc],mxv[rc]);
mis[k]=calc(mxv[rc],ls);
}
void build(int l,int r,int k)
{
if(l==r)return mxv[k]=a[l],void();
build(ls),build(rs),up(l,r,k);
}
void upd(int l,int r,int k,int p)
{
if(l==r)return mxv[k]=a[l],void();
p<=mid?upd(ls,p):upd(rs,p);up(l,r,k);
}
int main()
{
int n=in(),m=in(),op=in(),ans=0;
for(int i=1;i<=n;++i)
a[i]=a[i+n]=in(),a[i]-=i,a[n+i]-=n+i;
build(1,n<<1,1);printf("%d\n",ans=mis[1]+n-1);
for(int i=1;i<=m;++i)
{
int x=in()^op*ans,y=in()^op*ans;
a[x]=y-x;a[x+n]=y-x-n;
upd(1,n<<1,1,x),upd(1,n<<1,1,x+n);
printf("%d\n",ans=mis[1]+n-1);
}
return 0;
}
毒瘤
之前的博客是我没理解清写的。
树的\(dp\)是基础,然后把返祖边们抠出来建虚树,枚举两端情况。
这里的\(f[u][0/1]\)是表示\(u\)取\(0/1\)的时候,子树的方案数。
所以我们只要枚举返祖边的祖先点的状态就可以了。
然后处理转移系数\(xs[u][i=0/1][j=0/1]\)表示虚树上的父亲选\(i\),这个点选\(j\)的系数的转移系数。
注意到边是有影响的,所以不在虚树上的点的\(xs\)表示的是该点选\(i\),这个点子树内第一个虚点选\(j\)的系数,当我们发现这个\(xs\)转移到一个虚点时,直接把\(xs\)挂在后面那维代表的虚点上。
不在虚树上的点记得乘上这个点选\(0/1\)的方案数。
在虚树上的点的\(xs\)要使得\(xs[u][0][0]=xs[u][1][1]=1\)。
转移的时候如果这个点被强制选了某个值,另一的\(dp\)初值必须为\(0\)。
\(code\)
#include<vector>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define pb push_back
#define gt getchar()
#define ll long long
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
typedef std::pair<int,int> P;
#define mk std::make_pair
#define fr first
#define sc second
inline int in()
{
int k=0;char ch=gt;bool p=1;
while(ch<'-')ch=gt;if(ch=='-')ch=gt,p=0;
while(ch>'-')k=k*10+ch-'0',ch=gt;
return p?k:-k;
}
const int YL=998244353,N=1e5+5;typedef std::vector<int> vi;
inline int ksm(int a,int k){int r=1;while(k){if(k&1)r=1ll*r*a%YL;a=1ll*a*a%YL,k>>=1;}return r;}
inline int MO(const int &x){return x>=YL?x-YL:x;}
vi G[N],E[N];int xs[N][2][2],f[N][2],g[N][2],tsz[N],imp[N],o[N];
int Eu[N],Ev[N],tot,dep[N],fg[N][2],vis[N],tt;
void pre_dfs(int u,int pa=0)
{
o[u]=++tt;
for(int v:G[u])if(v==pa)continue;
else if(!o[v])pre_dfs(v,u),tsz[u]+=tsz[v];
else
{
imp[u]=1;
if(o[u]<o[v])
Eu[++tot]=u,Ev[tot]=v;
}
imp[u]|=tsz[u]>=2;tsz[u]=tsz[u]||imp[u];
}
void mul(int f[2][2],int g[2][2])
{
int f00=f[0][0],f01=f[0][1];
int f10=f[1][0],f11=f[1][1];
g[0][0]=MO(f00+f10);
g[0][1]=MO(f01+f11);
g[1][0]=f00,g[1][1]=f01;
}
inline void init(int f[2][2]){f[0][0]=f[1][1]=1,f[0][1]=f[1][0]=0;}
int dfs(int u)
{
vis[u]=g[u][0]=g[u][1]=1;int pos=0;
for(int v:G[u])
if(!vis[v])
{
int w=dfs(v);
if(!w)
{
g[u][0]=1ll*g[u][0]*(g[v][1]+g[v][0])%YL;
g[u][1]=1ll*g[u][1]*g[v][0]%YL;
}
else if(!imp[u])mul(xs[v],xs[u]),pos=w;
else mul(xs[v],xs[w]),E[u].pb(w),pos=w;
}
if(imp[u])return init(xs[u]),u;
xs[u][0][0]=1ll*xs[u][0][0]*g[u][0]%YL;
xs[u][0][1]=1ll*xs[u][0][1]*g[u][0]%YL;
xs[u][1][0]=1ll*xs[u][1][0]*g[u][1]%YL;
xs[u][1][1]=1ll*xs[u][1][1]*g[u][1]%YL;
return pos;
}
void dp(int u)
{
f[u][0]=fg[u][1]?0:g[u][0];
f[u][1]=fg[u][0]?0:g[u][1];
for(int v:E[u])
{
dp(v);
for(int i=0;i<2;++i)
f[u][i]=(1ll*xs[v][i][0]*f[v][0]+1ll*xs[v][i][1]*f[v][1])%YL*f[u][i]%YL;
}
}
int main()
{
int n=in(),m=in(),ans=0;
for(int i=1,u,v;i<=m;++i)
u=in(),v=in(),G[u].pb(v),G[v].pb(u);
pre_dfs(1),imp[1]=1,dfs(1);int mx=1<<tot;
for(int i=0;i<mx;++i)
{
for(int j=0;j<tot;++j)
if(i>>j&1)fg[Eu[j+1]][1]=1,fg[Ev[j+1]][0]=1;
else fg[Eu[j+1]][0]=1;
dp(1);ans=MO(ans+MO(f[1][1]+f[1][0]));
for(int j=0;j<tot;++j)
if(i>>j&1)fg[Eu[j+1]][1]=0,fg[Ev[j+1]][0]=0;
else fg[Eu[j+1]][0]=0;
}
printf("%d\n",ans);
return 0;
}
游戏
我们发现如果一个点能到\(L\),且另一个点能到它,那么另一个点肯定能到\(L\),所以我们预处理\(L[i],R[i]\)为\(i\)点能扩大的最大范围。我们需要安排一个顺序使得他们最优。
如果一扇门\(x,x+1\)的钥匙在\(1-x\)则肯定要先转移\(x+1\)再转移\(x\),反之亦然。
于是我们可以拓扑排序。
问题的关键在于门是不满的,所以有很多没有关系的点之间跳来跳去,复杂度就假了。
但是由于数据只卡了正着做的,没卡反着做的,于是他的乱搞就能过(他写了两篇乱搞(小声。
那我们用并查集把没有门的点缩起来就\(ok\)了。
\(code\)
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define gt getchar()
#define ll long long
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
typedef std::pair<int,int> P;
#define mk std::make_pair
#define fr first
#define sc second
inline int in()
{
int k=0;char ch=gt;bool p=1;
while(ch<'-')ch=gt;if(ch=='-')ch=gt,p=0;
while(ch>'-')k=k*10+ch-'0',ch=gt;
return p?k:-k;
}
const int N=1e6+5;
int head[N],to[N],du[N],p[N],L[N],R[N],nxt[N],cnt,key[N],tot,n,fa[N];
inline void add(int u,int v){to[++cnt]=v,nxt[cnt]=head[u],head[u]=cnt,++du[v];}
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
inline void work(int u)
{
int l=L[u],r=R[u],nl,nr;
while(1)
{
nl=l,nr=r;
while(l>1&&(!key[l-1]||(l<=key[l-1]&&key[l-1]<=r)))l=L[find(l-1)];
while(r<n&&(!key[ r ]||(l<=key[ r ]&&key[ r ]<=r)))r=R[find(r+1)];
if(nl==l&&nr==r)break;
}
L[u]=l,R[u]=r;
}
int main()
{
n=in();int m=in(),q=in();
for(int i=1,x,y;i<=m;++i)x=in(),y=in(),key[x]=y;
for(int i=1;i<=n;++i)fa[i]=i;
for(int i=1;i<n;++i)if(!key[i])fa[i+1]=find(i);
for(int i=1;i<n;++i)
if(key[i])
{
if(key[i]<=i)add(find(i+1),find(i));
else add(find(i),find(i+1));
}
std::queue<int>Q;
for(int i=1;i<=n;++i)if(fa[i]==i&&!du[i])Q.push(i);
while(!Q.empty())
{
int u=p[++tot]=Q.front();Q.pop();
for(int i=head[u];i;i=nxt[i])
if(!--du[to[i]])Q.push(to[i]);
}
for(int i=1;i<=n;++i)R[find(i)]=i;
for(int i=n;i>=1;--i)L[find(i)]=i;
for(int i=1;i<=tot;++i)work(p[i]);
while(q--){int x=find(in()),y=find(in());puts(L[x]<=y&&y<=R[x]?"YES":"NO");}
return 0;
}
排列
先把依赖关系的\(DAG\)建出来。
然后就是贪心,小的一定要尽量放在前面。
考虑当前最小值,它在父亲节点删掉后一定会被删。
所以可以并起来,然后我们现在是考虑一堆序列的顺序,推下式子发现只要平均值小就一定先选,就没了。
\(code\)
#include<cstdlib>
#include<vector>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define gt getchar()
#define ll long long
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define mk std::make_pair
#define fr first
#define sc second
#define double long double
typedef std::pair<double,int> P;
inline int in()
{
int k=0;char ch=gt;bool p=1;
while(ch<'-')ch=gt;if(ch=='-')ch=gt,p=0;
while(ch>'-')k=k*10+ch-'0',ch=gt;
return p?k:-k;
}
typedef std::vector<int> vi;
const int N=5e5+5;vi G[N];
const double eps=1e-6;
int o[N],sz[N],fa[N],ff[N];ll w[N];
struct Queue
{
std::priority_queue<P>Q1,Q2;
void push(P x){Q1.push(x);}
void erase(P x){Q2.push(x);}
void upd(){while(!Q2.empty()&&Q1.top()==Q2.top())Q1.pop(),Q2.pop();}
inline void pop(){upd();Q1.pop();}
inline P top(){upd();return Q1.top();}
}Q;
int dfs(int u)
{
int ans=u!=0;o[u]=1;
for(int v:G[u])
if(o[v])puts("-1"),exit(0);
else ans+=dfs(v);return ans;
}
int find(int x){return x==ff[x]?x:ff[x]=find(ff[x]);}
int main()
{
int n=in();ll ans=0;
for(int i=1;i<=n;++i)G[fa[i]=in()].push_back(i);
for(int i=1;i<=n;++i)ans+=w[i]=in();
if(dfs(0)!=n)return puts("-1"),0;
for(int i=1;i<=n;++i)ff[i]=i,sz[i]=1;
for(int i=1;i<=n;++i)Q.push(mk(-(double)w[i],i));
for(int i=1;i<=n;++i)
{
P now=Q.top();Q.pop();int u=find(now.sc),v=find(fa[u]);
if(v)Q.erase(mk(-(double)w[v]/sz[v],v));
ans+=w[u]*sz[v],w[v]+=w[u],sz[v]+=sz[u],ff[u]=v;
if(v)Q.push(mk(-(double)w[v]/sz[v],v));
}
printf("%lld\n",ans);
return 0;
}
道路
普及\(dp\),设\(F[i][j][k]\)表示第\(i\)个城市,没修的公路有\(j\)条,没修的铁路有\(k\)条的最小代价。
叶子节点直接算,非叶子节点枚举修什么。考场上好像卡空间,用分治的\(fft\)的卡空间技巧就行了
\(code\)
#include<vector>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define gt getchar()
#define ll long long
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
typedef std::pair<int,int> P;
#define mk std::make_pair
#define fr first
#define sc second
inline int in()
{
int k=0;char ch=gt;bool p=1;
while(ch<'-')ch=gt;if(ch=='-')ch=gt,p=0;
while(ch>'-')k=k*10+ch-'0',ch=gt;
return p?k:-k;
}
typedef std::vector<int> vi;
const int N=20005;vi G[N];
ll f[N][41][41],a[N],b[N],c[N];
ll dfs(int u,int L,int R)
{
if(u<0)return c[-u]*(a[-u]+L)*(b[-u]+R);
if(~f[u][L][R])return f[u][L][R];int lc=G[u][0],rc=G[u][1];
return f[u][L][R]=std::min(dfs(lc,L+1,R)+dfs(rc,L,R),dfs(lc,L,R)+dfs(rc,L,R+1));
}
int main()
{
int n=in();
for(int i=1;i<n;++i)
{
int s=in(),t=in();
G[i].push_back(s),G[i].push_back(t);
}
for(int i=1;i<=n;++i)a[i]=in(),b[i]=in(),c[i]=in();
memset(f,-1,sizeof f);printf("%lld\n",dfs(1,0,0));
return 0;
}
HNOI2018题解的更多相关文章
- # HNOI2012 ~ HNOI2018 题解
HNOI2012 题解 [HNOI2012]永无乡 Tag:线段树合并.启发式合并 联通块合并问题. 属于\(easy\)题,直接线段树合并 或 启发式合并即可. [HNOI2012]排队 Tag:组 ...
- HNOI2018简要题解
HNOI2018简要题解 D1T1 寻宝游戏 题意 某大学每年都会有一次 Mystery Hunt 的活动,玩家需要根据设置的线索解谜,找到宝藏的位置,前一年获胜的队伍可以获得这一年出题的机会. 作为 ...
- [HNOI2018]寻宝游戏(题解转载自别处)
题解(自别处转载): Luogu CSDN 这题关键是将运算符也替换成0,1 然后在运算符与原串混杂里找规律. 而且替换的方式也有所要求,考场上两种替换方式都要尝试. #include <bit ...
- 【题解】HNOI2018转盘
何学长口中所说的‘一眼题’……然而实际上出出来我大HN全省也只有一个人A…… 首先我们需要发现一个性质:我们永远可以在最后一圈去标记所有的物品.倘若我们反复转圈,那么这完全是可以省下来的.所以我们破环 ...
- 【题解】HNOI2018寻宝游戏
太厉害啦……感觉看到了正解之后整个人都惊呆了一样.真的很强%%% 首先要注意到一个性质.位运算列与列之间是不会相互影响的,那么我们先观察使一列满足条件的操作序列需要满足什么条件.&0时,不论之 ...
- 【BZOJ5285】[HNOI2018]寻宝游戏(神仙题)
[BZOJ5285][HNOI2018]寻宝游戏(神仙题) 题面 BZOJ 洛谷 题解 既然是二进制按位的运算,显然按位考虑. 发现这样一个关系,如果是\(or\)的话,只要\(or\ 1\),那么无 ...
- JLOI2015 DAY2 简要题解
「JLOI2015」骗我呢 题意 问有多少个 \(n \times m\) 的矩阵 \(\{x_{i, j}\}\) 满足 对于 \(\forall i \in [1, n], j \in [1, m ...
- 【BZOJ5289】[HNOI2018]排列(贪心)
[BZOJ5289][HNOI2018]排列(贪心) 题面 BZOJ 洛谷 题解 这个限制看起来不知道在干什么,其实就是找到所有排列\(p\)中,\(p_k=x\),那么\(k<j\),其中\( ...
- 【BZOJ5288】[HNOI2018]游戏(拓扑排序)
[BZOJ5288][HNOI2018]游戏(拓扑排序) 题面 BZOJ 洛谷 题解 去年省选的时候这题给我乱搞整过去整过去了,也是虐心了.... 所以当然是来讲正儿八经的正确做法啦. 很明显,我们需 ...
随机推荐
- 广电的宽带网络真流氓,替换google的广告为百度的广告
以前联通也有干过这事,最近联通,有没有继续干,不清楚.没有用联通了. 最近,连到某wifi,发现网站的google广告,居然显示成百度的,特别去访问另一家网站,发现,本该是google广告的位置,同样 ...
- 复习C#的方法Math.Max和Math.Min
温故而知新,今天学习Math.Max和Min的方法.这2个方法,均需要传入2个参数,返回参数中最大值和最小值. class Ac { public void LeanMathFunction() { ...
- (转)对一个deb包的解压、修改、重新打包全过程方法
转自:https://blog.csdn.net/yygydjkthh/article/details/36695243 Reference: http://www.debian.org/doc/ma ...
- Scala学习(四)练习
映射和元组&练习 1. 设置一个映射,其中包含你想要的一些装备,以及它们的价格.然后构建另一个映射,采用同一组键,但在价格上打9折 映射代码如下: object HelloScala{ def ...
- asp.net mvc 实现上传文件带进度条
本文乃是博主早期写的,此种思路虽然实现了,但固然不是最好的,仅做参考学习. 可以用js onprogress .fileinput .webuploader.jq ajaxsubmit等实现 思路:a ...
- Python基础(上)
前言 正式开始Python之旅,主要学习内容专注在爬虫和人工智能领域,如Web开发之类将跳过不研究. Python的意思是蟒蛇,源于作者Guido van Rossum(龟叔)喜欢的一部电视剧.所以现 ...
- 在线排错之curl命令详解
春回大地万物复苏,好久不来,向各位博友问好. 简介 cURL是一个利用URL语法在命令行下工作的文件传输工具,1997年首次发行.它支持文件上传和下载,所以是综合传输工具,但按传统,习惯称cURL为下 ...
- windows平台下编辑的内容传到linux平台出现中文乱码的解决办法
现象说明:在windows下编辑的内容,上传到linux平台下出现中文乱码.如下: 在windows平台编写haha.txt文件,内容如下: 上传到linux平台,出现中文乱码,如下: 基本上面出现的 ...
- Docker容器学习梳理 - 容器硬盘热扩容
前面已介绍了docker很多知识点的操作记录,今天这里梳理下docker容器空间扩展的操作.默认情况下,物理机下创建的docker容器的空间是10G(虚拟机下创建的docker容器空间就是虚拟机的空间 ...
- python报错问题解决:'ascii' codec can't encode character
之前部署了openstack虚拟化环境,有一天在使用nova list查看虚拟机的时候,突然报错!如下: [root@linux-node1 src]# nova listERROR (Unicode ...