NOIP模拟92&93(多校26&27)
前言
由于太菜了,多校26 只改出来了 T1 ,于是直接并在一起写啦~~~。
T0 NOIP 2018
解题思路
第一次考场上面写三分,然而我并不知道三分无法处理不是严格单峰的情况,但凡有一个平台都不行??
我们的贪心策略一定是尽量让我们选择的两个物品的值尽量接近,二分最后选择的一个价值,然后判断是否可行。
需要特判一下只选择一个的情况,以及两个个数相差一的情况,实现细节有一点多。。
code
#include<bits/stdc++.h>
#define int __int128
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
inline void write(int x)
{
#define sta number
int sta[70],top=0; if(x<0) putchar('-'),x=~(x-1);
while(x) sta[++top]=x%10,x/=10; if(!top) sta[++top]=0;
while(top) putchar(sta[top--]+'0'); putchar('\n');
#undef sta
}
int q,n,a,b,c,d,ans;
inline int g(int x,int y,int pos){return x*pos+y*(pos-1)*pos/2;}
inline long long g2(long long x,long long y,long long pos){return x*pos+y*(pos-1)*pos/2;}
inline int work(int x,int y){return (x-a)/b+(y-c)/d+2;}
bool check(int x)
{
int t1=max((int)0,(x-a)/b+1),t2=max((int)0,(x-c)/d+1),temp=g(a,b,t1)+g(c,d,t2);
if(temp<=n) return true;
if(t1>=1&&g(a,b,t1-1)+g(c,d,t2)<=n) ans=max(ans,t1+t2-1);
else if(t2>=1&&g(a,b,t1)+g(c,d,t2-1)<=n) ans=max(ans,t1+t2-1);
return false;
}
void sol(int x,int y)
{
int l=0,r=n,temp=-1;
while(l<=r)
{
int mid=(l+r)>>1,jud=false;
jud|=x*mid>n; jud|=g(x,y,mid)!=g2(x,y,mid);
jud|=g(x,y,mid)>n;
if(!jud) l=mid+1,temp=mid;
else r=mid-1;
}
ans=max(ans,temp);
}
void solve()
{
a=read(); b=read(); c=read(); d=read(); n=read();
int l=0,r=n,temp=-1; ans=0; sol(a,b); sol(c,d);
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)) l=mid+1,temp=mid;
else r=mid-1;
}
if(~temp) ans=max(ans,work(temp,temp)); write(ans);
}
#undef int
int main()
{
#define int long long
freopen("money.in","r",stdin); freopen("money.out","w",stdout);
q=read(); while(q--) solve();
return 0;
}
T1 开挂
解题思路
一个直接的想法就是给 \(b\) 排序然后给需要移动步数多的点较小的 \(b\) 。
我们肯定是要把一些重复的值填在一些空闲的值域里面,那么为了让答案尽量小,我们要让移动步数尽量不平均。
于是可以先把所有需要安排位置的数字放入一个栈,数值从栈低到栈顶单调不降。
对于一个空闲的值域,优先选择栈顶元素也就是较大的元素放入就好了..
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=1e6+10,INF=1e18;
int n,p,cnt,all,top,sta[N],a[N],b[N],c[N],s[N];
pair<int,int> res[N];
ull ans;
#undef int
int main()
{
#define int long long
freopen("openhook.in","r",stdin); freopen("openhook.out","w",stdout);
n=read(); a[n+1]=INF;
for(int i=1;i<=n;i++) a[i]=read(); sort(a+1,a+n+1);
for(int i=1;i<=n;i++) b[i]=read(); sort(b+1,b+n+1);
for(int i=1,len=1;i<=n;i++)
if(a[i]==a[i+1]) s[++all]=a[i];
else if(a[i+1]!=a[i]+1)res[++cnt]=make_pair(a[i]+1,a[i+1]-1);
for(int i=1,pos=0;i<=cnt;i++)
{
if(s[pos]>res[i].second) continue;
while(pos<all&&s[pos+1]<res[i].first) sta[++top]=s[++pos];
for(int j=res[i].first;j<=res[i].second&⊤j++)
{
c[++p]=j-sta[top--];
while(pos<all&&s[pos+1]<j+1) sta[++top]=s[++pos];
}
}
sort(c+1,c+n+1,greater<int>());
for(int i=1;i<=n;i++) ans+=c[i]*b[i];
printf("%llu",ans);
return 0;
}
T2 叁仟柒佰万
解题思路
子段中 \(mex\) 的值一定是全局的 \(mex\) 值。
设 \(f_i\) 表示前 \(i\) 的数字划分子段 \(mex\) 等于 \(1\sim n\) 的 \(mex\) 值的方案数。
假设全局 \(mex\) 值为 \(K\) ,那么转移方程就是 \(f_i=\sum\limits_{j=1}^{i-1}f_{j-1}\times[mex_{[i,j]}=K]\)
我们可以维护一个指针 \(pos\) 表示 \([1,pos]\) 的 \(mex\) 值都是 \(K\) 这个指针显然只会右移,同时在维护一下当前 \([pos,i]\) 区间的 \(mex\) 值用来维护。
实现细节稍多。
code
#include<bits/stdc++.h>
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=37e6+10,Num=262143,mod=1e9+7;
int T,n,pos,num,ans,X,Y,s[N],pre[N],cnt[N],mex;
bool vis[N];
void solve()
{
n=read(); ans=vis[0]=0; pos=num=mex=0; pre[0]=1; s[0]=N-1;
if(n!=N-10) for(int i=1;i<=n;i++) s[i]=read(),vis[i]=false;
else{X=read();Y=read();for(int i=2;i<=n;i++)s[i]=(1ll*s[i-1]*X+Y+i)&Num;}
for(int i=1;i<=n;i++) vis[s[i]]=true; while(vis[mex]) mex++;
if(n!=N-10) for(int i=0;i<=n;i++) cnt[i]=0;
for(int i=1;i<=n;i++)
{
int las=-1; cnt[s[i]]++; while(cnt[num]) num++;
while(num>=mex&&pos<=i){cnt[s[pos]]--; if(!cnt[s[pos]]&&s[pos]<num) las=num,num=s[pos]; pos++;}
if(~las) pos--,cnt[s[pos]]++,num=las; pre[i]=pre[i-1]; if(pos>=1) pre[i]=(pre[i]+pre[pos-1])%mod;
}
printf("%d\n",(pre[n]-pre[n-1]+mod)%mod);
}
int main()
{
freopen("clods.in","r",stdin); freopen("clods.out","w",stdout);
T=read(); while(T--) solve();
return 0;
}
T3 超级加倍
解题思路
有一种 Kruskal 重构树的感觉(好像从严格意义上来讲并不是),更多的开始能使一种笛卡尔树的东西吧。。
按照编号从大到小建树为 T1 从小到大建树为 T2 。
那么这两棵树的性质就是: 满足任意两点 x, y 在 T1 中的 lca 是路径最小值,在 T2 中是路径最大值。
那么符合条件的点对 \((x,y)\) 就需要满足 x 在 T1 中是 y 的祖先,y 在 T2 中是 x 的祖先。
直接在一棵树的 DFS 序上开树状数组,然后 DFS 另一棵树记录祖先的信息查询就好了。
code
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=2e6+10;
int n,dfn[N],siz[N];
long long ans;
vector<int> v[N];
struct Edge
{
int tot=1,tim,head[N],ver[N<<1],nxt[N<<1];
void add(int x,int y){ver[++tot]=y;nxt[tot]=head[x];head[x]=tot;}
void dfs(int x){dfn[x]=++tim;siz[x]=1;for(int i=head[x];i;i=nxt[i])dfs(ver[i]),siz[x]+=siz[ver[i]];}
}e1,e2;
struct DSU
{
int fa[N];
void init(){for(int i=1;i<=n;i++)fa[i]=i;}
int find(int x){if(fa[x]==x) return x;return fa[x]=find(fa[x]);}
void merge(int x,int y){fa[find(x)]=find(y);}
}D1,D2;
struct BIT
{
int tre[N];
#define lowbit(x) (x&(-x))
void insert(int x,int val){for(int i=x;i<=n;i+=lowbit(i))tre[i]+=val;}
int query(int x){int sum=0;for(int i=x;i;i-=lowbit(i))sum+=tre[i];return sum;}
int query(int l,int r){return query(r)-query(l-1);}
}T;
void dfs(int x)
{
ans+=T.query(dfn[x],dfn[x]+siz[x]-1); T.insert(dfn[x],1);
for(int i=e2.head[x];i;i=e2.nxt[i]) dfs(e2.ver[i]);
T.insert(dfn[x],-1);
}
int main()
{
freopen("charity.in","r",stdin); freopen("charity.out","w",stdout);
n=read(); read(); D1.init(); D2.init();
for(int i=2,x;i<=n;i++) x=read(),v[x].push_back(i),v[i].push_back(x);
for(int i=n;i>=1;i--) for(auto it:v[i]) if(it>i) e1.add(i,D1.find(it)),D1.merge(it,i);
for(int i=1;i<=n;i++) for(auto it:v[i]) if(it<i) e2.add(i,D2.find(it)),D2.merge(it,i);
e1.dfs(1); dfs(n); printf("%lld",ans);
return 0;
}
T4 欢乐豆
解题思路
正解是 线段树+最短路 ,但是貌似可以乱搞过。。
先把所有的关于 \(m\) 条边所涉及到的点记录下来,那么别的点到其他点的距离就是初始权值。
同时我们还需要再记录一下初始权值最小的点以保证我们再后面的最短路中对于附给大边权的情况可以跑回来。
然后对于每一个记录下来的点为源点跑最短路,一边跑一遍用并茶几维护,具体实现可以再向优先队列里面加入元素的时候记录一个 bool 值。
表示时候已经计算过了这个点到别的联通块的距离,就可以直接跑了。。。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=1e5+10,INF=1e18;
int n,m,mn=INF,mnid,ans,cnt,dis[N],sta[N],id[N],s[N],fa[N];
bool vis[N];
struct Node
{
int pos,dat,can;
bool friend operator < (Node x,Node y){return x.dat>y.dat;}
};
vector< pair<int,int> > v[N];
bool comp(Node x,Node y){return x.dat<y.dat;}
priority_queue<Node> q;
int find(int x){if(fa[x]==x) return x; return fa[x]=find(fa[x]);}
void New(int x){if(id[x]) return ; sta[++cnt]=x; id[x]=cnt;}
void insert(int x,int val)
{
if(fa[x]!=x) return ; fa[x]=x+1; dis[x]=val;
q.push((Node){x,dis[x]+s[sta[x]],true});
for(auto it:v[x]) if(dis[it.first]>dis[x]+it.second)
q.push((Node){it.first,dis[it.first]=dis[x]+it.second});
}
void solve(int fro)
{
for(int i=1;i<=cnt+1;i++) dis[i]=INF,fa[i]=i;
dis[fro]=0; q.push((Node){fro,0,false});
while(!q.empty())
{
Node temp=q.top(); int x=temp.pos; q.pop();
if(!temp.can){insert(x,temp.dat);continue;}
for(auto it:v[x]) vis[it.first]=true;
for(int i=find(1);i<=cnt;i=find(i+1)) if(!vis[i]) insert(i,temp.dat);
for(auto it:v[x]) vis[it.first]=false;
}
}
#undef int
int main()
{
#define int long long
freopen("happybean.in","r",stdin); freopen("happybean.out","w",stdout);
n=read(); m=read(); for(int i=1;i<=n;i++) s[i]=read();
for(int i=1,x,y,val;i<=m;i++)
x=read(),y=read(),val=read(),New(x),New(y),
v[id[x]].push_back(make_pair(id[y],val));
for(int i=1;i<=n;i++) if(!id[i]&&s[i]<mn) mn=s[i],mnid=i; if(mn!=INF) New(mnid);
for(int i=1;i<=n;i++) if(!id[i]) ans+=(n-1)*s[i];
for(int i=1;i<=cnt;i++)
{
int minn=INF; solve(i);
for(int j=1;j<=cnt;j++) ans+=dis[j],minn=min(minn,dis[j]+s[sta[j]]);
ans+=(n-cnt)*minn;
}
printf("%lld",ans);
return 0;
}
NOIP模拟92&93(多校26&27)的更多相关文章
- NOIP模拟92(多校25)
前言 所以说这次是 HZOI 多校联测巅峰????(题目,数据过水??) T1 石子合并 解题思路 签到题. 发现我们可以给每个数字附一个正负号,每个数字的贡献就是它本身乘上这个符号. 发现至少应该有 ...
- 2021.7.29考试总结[NOIP模拟27]
T1 牛半仙的妹子图 做法挺多的,可以最小生成树或者最短路,复杂度O(cq),c是颜色数. 我考场上想到了原来做过的一道题影子,就用了并查集,把边权排序后一个个插入,记录权值的前缀和,复杂度mlogm ...
- Noip模拟63 2021.9.27(考场惊现无限之环)
T1 电压机制 把题目转化为找那些边只被奇数环包含. 这样的话直接$dfs$生成一棵树,给每个点附上一个深度,根据其他的非树边都是返祖边 可以算出环内边的数量$dep[x]-dep[y]+1$,然后判 ...
- noip模拟26[肾炎黄·酱累黄·换莫黄]
\(noip模拟26\;solutions\) 这个题我做的确实是得心应手,为啥呢,因为前两次考试太难了 T1非常的简单,只不过我忘记了一个定理, T2就是一个小小的线段树,虽然吧我曾经说过我再也不写 ...
- noip模拟27[妹子图·腿·腰](fengwu半仙的妹子们)
\(noip模拟27\;solutions\) 这次吧,我本来以为我能切掉两个题,结果呢??只切掉了一个 不过,隔壁Varuxn也以为能切两个,可惜了,他一个都没切...... 确实他分比我高一点,但 ...
- NOIP模拟题17.9.26
B 君的任务(task)[题目描述]与君初相识,犹如故人归.B 君看到了Z 君的第一题,觉得很难.于是自己出了一个简单题.你需要完成n 个任务,第i 任务有2 个属性ai; bi.其中ai 是完成这个 ...
- Noip模拟10 2021.6.27
T1 入阵曲 好了,又一个考试败笔题. 也就是在那个时候,小 F 学会了矩阵乘法.让两个矩阵乘几次就能算出斐波那契数, 真是奇妙无比呢. 不过, 小 F 现在可不想手算矩阵乘法--他觉得好麻烦.取而代 ...
- 2021.7.28考试总结[NOIP模拟26]
罕见的又改完了. T1 神炎皇 吸取昨天三个出规律的教训,开场打完T2 20pts直接大力打表1h. 但怎么说呢,我不懂欧拉函数.(其实exgcd都忘了 于是只看出最大平方因子,不得不线性筛,爆拿60 ...
- NOIP 模拟4 T2
本题属于二和一问题 子问题相互对称 考虑对于问题一:知a求b 那么根据b数组定义式 显然能发现问题在于如何求dis(最短路) 有很多算法可供选择 dijsktra,floyed,bfs/dfs,spf ...
随机推荐
- setTimeout 与setInterval的区别
setTimeout(code,millisec) 方法用于在指定的毫秒数后调用函数或计算表达式 setInterval(code,millisec) 方法可按照指定的周期(以毫秒计)来调用函数或计算 ...
- 『GoLang』string及其相关操作
目录 1. 字符串简介 2. 字符串的拼接 3. 有关 string 的常用处理 3.1 strings 包 3.1.1 判断两个 utf-8 编码字符串是否相同 3.1.2 判断字符串 str 是否 ...
- P6076-[JSOI2015]染色问题【组合数学,容斥】
正题 题目链接:https://www.luogu.com.cn/problem/P6076 题目大意 给出\(n*m\)的网格,\(c\)种颜色涂色要求 每个格子可以染色也可以不染 每一行每一列至少 ...
- AT4119-[ARC096C]Everything on It【斯特林数,容斥】
正题 题目链接:https://www.luogu.com.cn/problem/AT4119 题目大意 一个集合\(S=\{k\in[1,n]\cup N\}\),它的所有子集作为元素组成的集合中要 ...
- 华为云计算IE面试笔记-请描述华为容灾解决方案全景图,并解释双活数据中心需要从哪些角度着手考虑双活设计
容灾全景图: 按照距离划分:分为本地容灾 同城容灾 异地容灾 本地容灾包括本地高可用和本地主备.(本数据中心的两机房.机柜) 本地高可用这个方案为了保持业务的连续性,从两个层面来考虑: ①一个是从主 ...
- webstorm 修改端口号
webstorm 修改端口号: 至此,点击下方 [apply],端口号修改完成.
- 易华录 X ShardingSphere|葫芦 App 后台数据处理的逻辑捷径
"ShardingSphere 大大简化了分库分表的开发和维护工作,对于业务的快速上线起到了非常大的支撑作用,保守估计 ShardingSphere 至少为我们节省了 4 个月的研发成本.& ...
- 题解 「CTSC2018暴力写挂」
题目传送门 题目大意 给出两个大小为 \(n\) 的树,求出: \[\max\{\text{depth}(x)+\text{depth}(y)-\text{depth}(\text{LCA}(x,y) ...
- bzoj1972 SDOI2010-----猪国杀(模拟)
题目自己去找吧 记得数据范围是<=10 注意事项: 1.牌库空的时候,要不断的抽第一张牌 2.反贼的决斗永远是向主公发的 3.每次判定无懈的时候,都是从使用锦囊的那个牌开始,记得敌意和殷勤的判断 ...
- 2020.12.20-Codeforces Round #105补题
B - Escape The princess is going to escape the dragon's cave, and she needs to plan it carefully. Th ...