HNOI 2018 简要题解
寻宝游戏
毒瘤题。
估计考试只会前30pts30pts30pts暴力然后果断走人。
正解是考虑到一个数&1\&1&1和∣0|0∣0都没有变化,&0\&0&0会强制变成000,∣1|1∣1会强制变成111,于是如果结果是111说明最后一个出现的∣1|1∣1在最后一个出现的&0\&0&0的后面,这样我们将所有的操作集合拿来搞成一个010101串并把所有010101串反向之后就可以求出可行范围然后得出答案了。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<1)+(ans<<3)+(ch^48),ch=getchar();
return ans;
}
const int mod=1e9+7,N=5005;
int n,m,q,Bit[N],sum[N],c[2],rk[N],lastrk[N],l,r;
char s[N];
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline void update(int&a,const int&b){a=add(a,b);}
inline void init(){
for(ri i=Bit[0]=1;i<=n;++i)Bit[i]=(Bit[i-1]<<1)%mod;
for(ri i=1;i<=m;++i)rk[i]=i;
}
inline void Sort(){
for(ri i=c[0]=c[1]=0;i<n;++i,c[0]=c[1]=0){
scanf("%s",s+1);
for(ri j=1;j<=m;++j)++c[s[j]^48],update(sum[j],(s[j]^48)*Bit[i]);
c[1]+=c[0];
for(ri j=m;j;--j)lastrk[c[s[rk[j]]^48]--]=rk[j];
swap(lastrk,rk);
}
}
int main(){
n=read(),m=read(),q=read(),init(),Sort();
while(q--){
scanf("%s",s+1),l=0,r=m+1;
for(ri i=1;i<=m;++i)if(s[rk[i]]^48){r=i;break;}
for(ri i=m;i;--i)if(!(s[rk[i]]^48)){l=i;break;}
l>=r?cout<<0<<'\n':cout<<(dec(r>m?Bit[n]:sum[rk[r]],sum[rk[l]]))<<'\n';
}
return 0;
}
转盘
考虑时间倒流转化问题:
想象成从TTT时刻开始每个时刻可以倒着走一步或者停住,每个物品有一个消失时间,要在所有物品消失之前经过它们。
为了方便我们断环为链
假设是从iii开始倒退(n≤i<2n)(n\le i<2n)(n≤i<2n),则有T−(i−j)≥tj⇒T≥(tj−j)+i⇒Tmin=max{Tj−j}+iT-(i-j)\ge t_j\Rightarrow T\ge(t_j-j)+i\Rightarrow T_{min}=\max\{T_j-j\}+iT−(i−j)≥tj⇒T≥(tj−j)+i⇒Tmin=max{Tj−j}+i。
令ai=ti−ia_i=t_i-iai=ti−i
那么Ans=minn≤i<2n{maxi−n<j≤i{aj}+i}Ans=min_{n\le i<2n}\{max_{i-n<j\le i}\{a_j\}+i\}Ans=minn≤i<2n{maxi−n<j≤i{aj}+i}
因为ai>ai+n,i≤na_i>a_{i+n},i\le nai>ai+n,i≤n
所以Ans=min1≤i≤n{maxi≤j≤2n{aj}+i}+n−1Ans=min_{1\le i\le n}\{max_{i\le j\le 2n}\{a_j\}+i\}+n-1Ans=min1≤i≤n{maxi≤j≤2n{aj}+i}+n−1
然后发现就是唯一一个后缀maxmaxmax的minminmin,这个可以用线段树维护单调栈实现。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<1)+(ans<<3)+(ch^48),ch=getchar();
return ans;
}
const int N=1e5+5,inf=0x3f3f3f3f;
int n,m,lastans,a[N<<1],type;
namespace SGT{
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (T[p].l+T[p].r>>1)
struct Node{int l,r,val,mx;}T[N<<3];
inline int query(int p,int lim,int v){
if(T[p].l==T[p].r)return min(T[p].l==lim?inf:T[p].l+1+v,T[p].l+max(v,T[p].mx));
if(v>=T[rc].mx)return query(lc,lim,v);
return min(T[p].val,query(rc,lim,v));
}
inline void pushup(int p){T[p].mx=max(T[lc].mx,T[rc].mx),T[p].val=query(lc,mid,T[rc].mx);}
inline void build(int p,int l,int r){
T[p].l=l,T[p].r=r;
if(l==r){T[p].mx=a[l];return;}
build(lc,l,mid),build(rc,mid+1,r),pushup(p);
}
inline void update(int p,int k,int v){
if(T[p].l==T[p].r){T[p].mx=v;return;}
update(k<=mid?lc:rc,k,v),pushup(p);
}
#undef lc
#undef rc
#undef mid
}
int main(){
n=read(),m=read(),type=read();
for(ri i=1;i<=n;++i)a[i]=read()-i,a[i+n]=a[i]-n;
SGT::build(1,1,n<<1);
cout<<(lastans=SGT::T[1].val+n-1)<<'\n';
for(ri x,y;m;--m){
x=read()^(type*lastans),y=read()^(type*lastans);
SGT::update(1,x,y-x),SGT::update(1,x+n,y-x-n);
cout<<(lastans=SGT::T[1].val+n-1)<<'\n';
}
return 0;
}
毒瘤
枚举边的状态+树形dpdpdp这种暴力757575应该都是一眼会吧。
但这样会TLETLETLE掉最后几个点 出题人毒瘤
然后需要建出这棵树的虚树来优化每次dpdpdp的时间。
然后就没了。
代码:
#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<1)+(ans<<3)+(ch^48),ch=getchar();
return ans;
}
typedef pair<int,int> pii;
typedef long long ll;
const int N=1e5+5,mod=998244353;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
int P[N][2],n,m,ans=0,f[N][2],siz[N],tot=0,dfn[N];
vector<int>e[N];
vector<pii>G;
bool vis[N],key[N],ban[N][2];
void Dfs(int p,int fa){
dfn[p]=++tot;
for(ri i=0,v;i<e[p].size();++i){
if((v=e[p][i])==fa)continue;
if(dfn[v]){key[p]=1;if(dfn[p]<dfn[v])G.push_back(pii(p,v));continue;}
else Dfs(v,p),siz[p]+=siz[v];
}
key[p]|=siz[p]>=2;
siz[p]=siz[p]||key[p];
}
struct Coef{
int x,y;
Coef(int _x=0,int _y=0):x(_x),y(_y){}
friend inline Coef operator+(const Coef&a,const Coef&b){return Coef(add(a.x,b.x),add(a.y,b.y));}
friend inline Coef operator*(const Coef&a,const int&b){return Coef(mul(a.x,b),mul(a.y,b));}
}k[N][2];
struct Node{int v;Coef a,b;};
vector<Node>E[N];
inline int Build(int p){
P[p][0]=P[p][1]=vis[p]=1;
int ret=0;
for(ri w,i=0,v;i<e[p].size();++i){
if(vis[v=e[p][i]])continue;
w=Build(v);
if(!w)P[p][1]=mul(P[p][1],P[v][0]),P[p][0]=mul(P[p][0],add(P[v][0],P[v][1]));
else if(key[p])E[p].push_back((Node){w,k[v][0]+k[v][1],k[v][0]});
else k[p][0]=k[v][0]+k[v][1],k[p][1]=k[v][0],ret=w;
}
if(key[p])k[p][0]=Coef(1,0),k[p][1]=Coef(0,1),ret=p;
else k[p][0]=k[p][0]*P[p][0],k[p][1]=k[p][1]*P[p][1];
return ret;
}
inline void solve(int p){
f[p][0]=ban[p][0]?0:P[p][0];
f[p][1]=ban[p][1]?0:P[p][1];
for(ri i=0,v;i<E[p].size();++i){
solve((v=E[p][i].v));
f[p][0]=mul(f[p][0],add(mul(E[p][i].a.x,f[v][0]),mul(E[p][i].a.y,f[v][1])));
f[p][1]=mul(f[p][1],add(mul(E[p][i].b.x,f[v][0]),mul(E[p][i].b.y,f[v][1])));
}
}
inline void init(int sta){
for(ri i=0,x,y,tmp;i<G.size();++i){
tmp=(sta>>i)&1,x=G[i].fi,y=G[i].se;
if(!tmp)ban[x][1]=1;
else ban[x][0]=ban[y][1]=1;
}
solve(1),ans=add(ans,add(f[1][0],f[1][1]));
for(ri i=0,x,y,tmp;i<G.size();++i){
tmp=(sta>>i)&1,x=G[i].fi,y=G[i].se;
if(!tmp)ban[x][1]=0;
else ban[x][0]=ban[y][1]=0;
}
}
int main(){
n=read(),m=read();
for(ri i=1,u,v;i<=m;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
Dfs(1,0),key[1]=1,Build(1);
for(ri sta=0,up=1<<G.size();sta<up;++sta)init(sta);
cout<<ans;
return 0;
}
游戏
对于两个可以不能相通的块,如果钥匙在左边,那么可能可以从左到右但并不能从右到左,我们对于这种情况建一条有向边,然后动态维护一个拓扑排序即可。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
const int N=1e6+5;
int n,m,q,goa[N],l[N],r[N],mp[N],du[N],tot=0,x,y;
vector<int>e[N];
inline void add(const int&u,const int&v){e[u].push_back(v),++du[v];}
inline void extend(int i){
static int t1,t2;
while(1){
t1=l[i]^1,t2=r[i]^n;
if(t1)(!goa[l[i]-1])||(goa[l[i]-1]>=l[i]&&goa[l[i]-1]<=r[i])?l[i]=l[mp[l[i]-1]]:t1=0;
if(t2)(!goa[r[i]])||(goa[r[i]]>=l[i]&&goa[r[i]]<=r[i])?r[i]=r[mp[r[i]+1]]:t2=0;
if(!(t1+t2))break;
}
}
inline void topsort(){
static int q[N],hd,tl;
hd=1,tl=0;
for(ri i=1;i<=n;i++)if(mp[i]==i&&!du[i])q[++tl]=i;
while(hd<=tl){
int p=q[hd++];
extend(p);
for(ri i=0,v;i<e[p].size();++i)if(!(--du[v=e[p][i]]))q[++tl]=v;
}
}
int main(){
n=read(),m=read(),q=read();
for(ri i=1;i<=n;i++)mp[i]=l[i]=r[i]=i;
while(m--)x=read(),goa[x]=read();
for(ri i=1;i<=n;i++)if(!goa[i])mp[i+1]=mp[i];else goa[i]<=i?add(mp[i+1],mp[i]):add(mp[i],mp[i+1]);
for(ri i=1;i<=n;i++)l[mp[i]]=min(l[mp[i]],l[i]),r[mp[i]]=max(r[mp[i]],r[i]);
topsort();
for(ri i=1;i<=n;i++)l[i]=l[mp[i]],r[i]=r[mp[i]];
while(q--)x=read(),y=read(),puts(l[x]<=y&&y<=r[x]?"YES":"NO");
return 0;
}
排列
考虑按照题意建边建出来是一棵外向树。
然后显然应该按某一种拓扑序来形成序列(注意这个时候要判环来看是否合法)
这个东西可以用并查集+堆来搞一下,比较的依据是一个连通块的平均权值。
代码:
#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<1)+(ans<<3)+(ch^48),ch=getchar();
return ans;
}
const int N=5e5+5;
typedef long long ll;
typedef pair<long double,int> pii;
int n,m,siz[N],fa[N],anc[N];
ll w[N],ans=0;
inline int find(const int&x){return x^anc[x]?anc[x]=find(anc[x]):x;}
struct In_Out_queue{
priority_queue<pii,vector<pii>,greater<pii> >a,b;
inline void push(const pii&x){a.push(x);}
inline void del(const pii&x){b.push(x);}
inline pii top(){while(b.size()&&a.top()==b.top())a.pop(),b.pop();return a.top();}
inline void pop(){while(b.size()&&a.top()==b.top())a.pop(),b.pop();a.pop();}
}q;
int main(){
n=read();
for(ri i=0;i<=n;++i)anc[i]=i;
for(ri i=1;i<=n;++i){
fa[i]=read();
if(anc[find(i)]^anc[find(fa[i])])anc[find(i)]=find(fa[i]);
else return puts("-1"),0;
}
ll ans=0;
anc[0]=0;
for(ri i=1;i<=n;++i)ans+=(w[i]=read()),anc[i]=i,siz[i]=1,q.push(pii((long double)w[i],i));
for(ri i=1;i<=n;++i){
int x=q.top().se,y=find(fa[x]);
q.pop();
if(y)q.del(pii((long double)w[y]/siz[y],y));
ans+=(ll)siz[y]*w[x],siz[y]+=siz[x],anc[x]=y,w[y]+=w[x];
if(y)q.push(pii((long double)w[y]/siz[y],y));
}
cout<<ans;
return 0;
}
道路
HNOI2018最水的一道没有之一没错就是它。 并不是反话
由于题目中给了深度保证,于是直接暴力三维树形dpdpdp就能过了。
记忆化搜索真的好写
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
inline int read(){
int ans=0;
bool f=1;
char ch=getchar();
while(!isdigit(ch))f^=ch=='-',ch=getchar();
while(isdigit(ch))ans=(ans<<1)+(ans<<3)+(ch^48),ch=getchar();
return f?ans:-ans;
}
const int N=20005;
ll a[N],b[N],c[N],f[N][45][45];
int son[N][2],n;
ll dfs(int p,int x,int y){
if(p>=n)return c[p-n+1]*(a[p-n+1]+x)*(b[p-n+1]+y);
if(f[p][x][y]^f[0][0][0])return f[p][x][y];
return f[p][x][y]=min(dfs(son[p][0],x,y)+dfs(son[p][1],x,y+1),dfs(son[p][0],x+1,y)+dfs(son[p][1],x,y));
}
int main(){
n=read();
memset(f,127,sizeof(f));
for(ri i=1,x,y;i<n;++i){
x=read(),y=read();
if(x<0)x=n-x-1;
if(y<0)y=n-y-1;
son[i][0]=x,son[i][1]=y;
}
for(ri i=1;i<=n;++i)a[i]=read(),b[i]=read(),c[i]=read();
return cout<<dfs(1,0,0),0;
}
HNOI 2018 简要题解的更多相关文章
- JXOI 2018 简要题解
目录 「JXOI2018」游戏 题意 题解 代码 「JXOI2018」守卫 题意 题解 代码 「JXOI2018」排序问题 题意 题解 代码 总结 「JXOI2018」游戏 题意 可怜公司有 \(n\ ...
- NOIP 2018 简要题解
从这里开始 Day 1 Problem A 考虑贪心地选取极大非 0 段减少. 如果两次操作有交,并且不是包含关系,那么把其中一次操作的,但另一次没有操作的移过去,然后就变成了上面那个贪心了. Cod ...
- codechef February Challenge 2018 简要题解
比赛链接:https://www.codechef.com/FEB18,题面和提交记录是公开的,这里就不再贴了 Chef And His Characters 模拟题 Chef And The Pat ...
- HNOI2019 简要题解
HNOI 2019 简要题解 没想到自己竟也能有机会写下这篇题解呢. LOJ Luogu Day1T1 鱼 枚举\(AD\)两点后发现\(BC\)与\(EF\)相对独立,因此只需要计算合法的\(BC\ ...
- Tsinghua 2018 DSA PA2简要题解
反正没时间写,先把简要题解(嘴巴A题)都给他写了记录一下. upd:任务倒是完成了,我也自闭了. CST2018 2-1 Meteorites: 乘法版的石子合并,堆 + 高精度. 写起来有点烦貌似. ...
- HNOI2018简要题解
HNOI2018简要题解 D1T1 寻宝游戏 题意 某大学每年都会有一次 Mystery Hunt 的活动,玩家需要根据设置的线索解谜,找到宝藏的位置,前一年获胜的队伍可以获得这一年出题的机会. 作为 ...
- CQOI2018简要题解
CQOI2018简要题解 D1T1 破解 D-H 协议 题意 Diffie-Hellman 密钥交换协议是一种简单有效的密钥交换方法.它可以让通讯双方在没有事先约定密钥(密码)的情况下,通过不安全的信 ...
- A · F · O —— JLOI2018翻车记(附Day1简要题解)
JLOI2018翻车记 并不知道该怎么写... 算了还是按照标准剧情来吧 这应该是一篇写得非常差的流水账... 2018.04.04 Day -1 省选前在机房的最后一天. 压力并不是很大,毕竟联赛 ...
- Noip 2014酱油记+简要题解
好吧,day2T1把d默认为1也是醉了,现在只能期待数据弱然后怒卡一等线吧QAQ Day0 第一次下午出发啊真是不错,才2小时左右就到了233,在车上把sao和fate补掉就到了= = 然后到宾馆之后 ...
随机推荐
- 初识docker-镜像
前言: 以前学习docker 都是零零碎碎的,只知道用,有些莫名其妙的报错自己也没有思路去解决,所以基于一本专业的介绍docker的书籍,重新开启学习,该博客就记录下我自己的学习过程吧. 1.dock ...
- html/css/js-横向滚动条的实现
在前端UI设计时,网页的制作很麻烦,深有感悟!碰到太多的不懂,或是第一次见,就要去网上找资料!横向滚动条就是我遇到麻烦中其中的一个,其实也 很简单,只是在几次项目中都用到了这个横向滚动条所以就拿出来说 ...
- Java访问Phoenix连接
两种方法,一种是直接使用jdbc连接,一种是使用spring连接. jdbc连接和访问oracle步骤相同: ///////////// 测试Phoenix连接 /////////////// Str ...
- ETL讲解(很详细!!!)
ETL讲解(很详细!!!) ETL是将业务系统的数据经过抽取.清洗转换之后加载到数据仓库的过程,目的是将企业中的分散.零乱.标准不统一的数据整合到一起,为企业的决策提供分析依据. ETL是BI项目重要 ...
- Android SDK + Appium 环境搭建
一.JDK 安装 说明:JDK是包含了JAVA的运行环境(JVM+Java系统类库)和JAVA工具,所以必须最先安装. 链接: https://pan.baidu.com/s/1NfNK_K7vukF ...
- python大法好——递归、内置函数、函数进阶
1.递归(自己干自己) def fun(n): n=n/2 print(n) if(n>2) fun(n) #函数调用 fun(10) 结果是5 2 1 0 递归的执行过程:递归执行到最里面一层 ...
- mybatis sql参考
参考mybatis sql: <select id="xxx" resultType="com.xxxx.xxx.vo.xx.xx" parameterT ...
- [Redis]Redis高级特性的配置及使用
---------------------------------------------------------------------------- [Redis安全性] 一 . 默认我们进入Re ...
- Delphi TXLSReadWriteII 导出EXCEL
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms ...
- Java获取工程目录
背景:程序执行时,会涉及到去读取配置文件等操作,那就需要了解怎么获得文件路径 Java目录映射关系 说明一点:在Java代码执行时,会将编译生成的classes文件,以及配置文件等信息生成到tar ...