NOIP提高组模拟赛18
求和
等差数列求和,直接乘会炸,需要龟速乘(考试时候懒得打高精主要是不会,也忘了有龟速乘这个东西.......)
code
#include<cstdio>
#include<cstring>
using namespace std;
typedef unsigned long long ull;
ull x,y,x2,y2,mod;
ull slow(ull x,ull y){
ull ans=0;
while(y){
if(y&1)ans=(ans+x)%mod;
x=(x+x)%mod;
y>>=1;
}
return ans;
}
int main()
{
freopen("sum.in","r",stdin);
freopen("sum.out","w",stdout);
scanf("%llu%llu%llu%llu%llu",&x,&y,&x2,&y2,&mod);
ull ans=(x+y-1);
ull h=x2-x+1;
ull z=y2-y+1;
ull ghs=slow(h,z);
ans=slow(ans,ghs);
ull h1=h-1,h2=h;
if(h&1)h1>>=1;
else h2>>=1;
ull yh=slow(h1,h2);
ans=(ans+slow(yh,z))%mod;
ull z1=z-1,z2=z;
if(z&1)z1>>=1;
else z2>>=1;
ull res=slow(z2,z1);
res=slow(res,h);
ans=(ans+res)%mod;
printf("%llu\n",ans);
return 0;
}
B. 分组配对
有个贪心就是能多选一定多选,然后因为区间连续,就从左边开始判断即可。
因为排序不等式,最大的与最大的相乘,次大的与次大的相乘......这样得到的是最大值
显然满足二分性,但是直接二分会$TLE4
优化是倍增+二分,看起来复杂了,但是时间复杂度确实小了
先倍增锁定二分区间,然后二分,不变
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=500005;
ll n,m;
ll a[maxn],b[maxn];
ll aa[maxn],bb[maxn];
bool check(int l,int r){
for(int i=l;i<=r;++i)aa[i]=a[i];
for(int i=l;i<=r;++i)bb[i]=b[i];
sort(aa+l,aa+r+1);
sort(bb+l,bb+r+1);
bool flag=1;
ll sum=0;
for(int i=l;i<=r;++i){
sum+=1ll*aa[i]*bb[i];
if(sum>m){
flag=0;
break;
}
}
return flag;
}
int work(){
int now=1,ans=0;
while(now<=n){
int l=now,r=n;
for(int i=1;now+(1<<i)-1<=n;++i)
if(!check(now,now+(1<<i)-1)){
l=now+(1<<(i-1))-1;
r=now+(1<<i)-1;
break;
}
while(l<r){
int mid=(l+r+1)>>1;
if(check(now,mid))l=mid;
else r=mid-1;
}
++ans;
now=l+1;
}
return ans;
}
int main()
{
freopen("pair.in","r",stdin);
freopen("pair.out","w",stdout);
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
for(int i=1;i<=n;++i)scanf("%lld",&b[i]);
printf("%d\n",work());
return 0;
}
C. 城市游戏
神奇的,有的地方写的挺假的,有些情况根本没考虑,但是仍然能,大佬想但是根本不掉,我也是挺迷的
解法倒是没啥大问题
设表示从号点走到号点,之前没有封过道路的最短距离
小在号点,考虑这条边,如果封路,那么处理出不走这条边到达的最短路来更新答案,如果不封路,那么就是更新答案,因为采取最优策略所以取
即
这是带环的,使用计算答案
如何求解
最短路径树的思想结合并查集进行解决
具体方法如下:
以为原点,进行一次最短路算法,并且在每个点上记录,从到这个点的最
短路径的前驱,前驱有多个的话记录任意一个即可
构造一张新图,每个点与自己记录的前驱,边的长度为在原图中这两个点之间的边的长度
这样的连边过后会构造出一棵以点𝑛为根的有根树,被称为
最短路径树具有很多优秀的性质,其中最重要的一条性质是:树上的两个点如果具有父子关系,则树上这两个点之间的路径对应于原图中这两个点之间的一条最短路
在求解 时,只有边 出现在树上的时候,这样的求解才有
意义。否则 就等于原图中到的最短路长度
所以,余下的任务是:对树上的每条边,求出它被删掉后,从点𝑛到这条边连接着的儿子的最短路长度
考虑删掉的边是 其中为父亲,为儿子。在 被删掉以后,树被分为了两个连通块,一个是以𝑣为根的子树,另一个是剩余的部分
如果想从点移动到点,一定会经过一条跨越了两个连通块的边
设这条边连接着第一个连通块内的点,和第二个连通块的点
那么,就有一条从点移动到点的路径
路径的长度是
其中 表示点到点的最短路长度
可以发现,上面的表达式中,前面几项和边 𝑎,𝑏 相关,而最后一项和有关。
设
则在枚举了点𝑣后,我们的标是找到一条不在树上的边使最小,且满足在的子树中而不在
转变一个思路来求解上述问题。枚举边,再考虑哪些点是可以选择的
可以发现,可行的点应该在的树链上,且不能是和的
如果暴力地进行上述操作,时间复杂度不能接受(实际上能过)
如果我们把边按进行排序,按顺序对每条边的树链上的点(不包括)进行赋值,那么被赋值过一次的点就不需要再被赋值。
以此条件进行优化:设表示及的祖先中,深度最大的还没被赋值过的点。每次点被赋值后,就执行
此时的并不是实时更新的,而是在我们需要获取的准确值时,不
断查看,...,直到找到这玩意就跟并查集路径压缩一个道理
code
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=100005;
const int maxm=200005;
const ll inf=0x3f3f3f3f3f3f3f3f;
priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > >q;
struct fsb{int a,b;ll val;}fb[maxm<<2|1];
struct edge{int val,net,to;}e[maxm<<1|1];
struct ed{int net,to;}ee[maxn];
bool cmp(fsb x,fsb y){return x.val<y.val;}
int n,m,cnt,rem[maxn],head[maxn],tot=1,he[maxn],tott,f[maxn],fa[maxn][19],dep[maxn];
ll dv[maxn],d[maxn],ans[maxn];
bool vis[maxn],is[maxm<<1|1];
void add(int u,int v,int w){
e[++tot].net=head[u];head[u]=tot;
e[tot].val=w;e[tot].to=v;
}
void ad(int u,int v){
ee[++tott].net=he[u];
he[u]=tott;ee[tott].to=v;
}
void dfs(int x){
for(int i=1;i<=18;++i)fa[x][i]=fa[fa[x][i-1]][i-1];
dep[x]=dep[fa[x][0]]+1;
for(int i=he[x];i;i=ee[i].net){
fa[ee[i].to][0]=x;
dfs(ee[i].to);
}
}
int LCA(int u,int v){
if(dep[u]<dep[v])swap(u,v);
int ddep=dep[u]-dep[v];
for(int i=18;i>=0;--i)if((1<<i)<=ddep)ddep-=(1<<i),u=fa[u][i];
if(u==v)return u;
for(int i=18;i>=0;--i)if(fa[u][i]!=fa[v][i])u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
int gf(int x){
if(f[x]==x)return x;
return f[x]=gf(f[x]);
}
void dij(){
d[n]=0;q.emplace(0,n);
while(!q.empty()){
int x=q.top().second;q.pop();
if(vis[x])continue;
for(int i=head[x];i;i=e[i].net){
int v=e[i].to;
if(d[v]>d[x]+e[i].val){
d[v]=d[x]+e[i].val;
rem[v]=i;
q.emplace(d[v],v);
}
}
}
}
void DIJ(){
memset(vis,0,sizeof(vis));
ans[n]=0;q.emplace(0,n);
while(!q.empty()){
int x=q.top().second;q.pop();
if(vis[x])continue;vis[x]=1;
for(int i=head[x];i;i=e[i].net){
int v=e[i].to;
//if(ans[v]>max(ans[x]+e[i].val,is[i]?dv[v]:d[v])){
if(ans[v]>max(ans[x]+e[i].val,dv[v])){
//上边两个if都能AC,而且对拍拍不出错误,不是很理解,下面的这个显然是错误的,为什么答案是对的?
//是因为非树边一定不会更新答案吗?但是直接不考虑非树边也是错的
//更新出了非法状态但是得到了正确解???
ans[v]=max(ans[x]+e[i].val,dv[v]);
q.emplace(ans[v],v);
}
}
}
}
int main()
{
freopen("city.in","r",stdin);
freopen("city.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=0;i<=n;++i)d[i]=inf;
for(int i=0;i<=n;++i)ans[i]=inf;
for(int i=0;i<=n;++i)dv[i]=inf;
for(int i=1;i<=m;++i){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
dij();
for(int i=1;i<n;++i){
ad(e[rem[i]^1].to,i);
is[rem[i]]=is[rem[i]^1]=1;
}
dfs(n);
for(int i=1;i<=n;++i)f[i]=i;
for(int i=2;i<=tot;i+=2){
if(is[i])continue;
int u=e[i^1].to,v=e[i].to;
fb[++cnt].val=e[i].val+d[u]+d[v];
fb[cnt].a=u;fb[cnt].b=v;
}
sort(fb+1,fb+cnt+1,cmp);
for(int i=1;i<=cnt;++i){
int lca=LCA(fb[i].a,fb[i].b);
int x=gf(fb[i].a);
while(dep[x]>dep[lca]){
dv[x]=fb[i].val-d[x];
f[x]=fa[x][0];
x=gf(x);
}
x=gf(fb[i].b);
while(dep[x]>dep[lca]){
dv[x]=fb[i].val-d[x];
f[x]=fa[x][0];
x=gf(x);
}
}
DIJ();
if(ans[1]==ans[0])printf("-1\n");
else printf("%lld\n",ans[1]);
return 0;
}
简单计算
数论牛逼题。。。
题解几行,完了。。。。。然而他确实就这么简单,但是确实难想(还是我太菜)
最后一步简单说一下
有
则
那么有个满足条件的
code
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
ll p,q;
ll gcd(ll x,ll y){
return y==0?x:gcd(y,x%y);
}
int main()
{
freopen("simplecalc.in","r",stdin);
freopen("simplecalc.out","w",stdout);
int T;scanf("%d",&T);
for(int ask=1;ask<=T;++ask){
scanf("%lld%lld",&p,&q);
printf("%lld\n",((p+1)*q-p+gcd(p,q))/2);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】