[CSP模拟测试43、44]题解
状态极差的两场。感觉现在自己的思维方式很是有问题。
(但愿今天考试开始的一刻我不会看到H I J)
A
考场上打了最短路+贪心,水了60。
然而正解其实比那30分贪心好想多了。
进行n次乘法后的结果一定可以化成$S\times b^n + m\times a$的形式,并且$m$是b的若干次幂(带系数)之和。
也就是说,$m=\frac{T-S\times b^n}{a}$可以写成$b$进制数,当然前提是$T-S \times b^n \ mod\ a=0$。
那么这个b进制数的系数之和其实就是加法操作的次数,这个很好理解。
枚举乘法次数,然后得到相应的$m$后直接$b$进制拆解,注意要从高次开始。
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
ll S,T,a,b,ans=1e15;
int main()
{
scanf("%lld%lld%lld%lld",&S,&T,&a,&b);
for(ll n=0,now=1;S*now<=T;n++,now*=b)
{
ll m=T-S*now,res=0;
if(m%a)continue;
m/=a;
ll c=now;
while(m)res+=m/c,m%=c,c/=b;
res+=n;
ans=min(ans,res);
}
cout<<ans<<endl;
return 0;
}
B.
首先可以想到一个比较暴力的dp:$f[i][j]$表示前$i$个变量乘积为$j$的方案数,枚举上一个结果和当前变量的值$O(p^2)$转移即可。
正解只理解了思想,但转移方程仍然不是很懂。
打表很容易发现一个性质:若$gcd(a,P)=gcd(b,P)$,那么$f[i][a]=f[i][b]$。
显然出题人居心叵测,把两个状态转移方程的条件写反了23333。
不过话说$\varphi(\frac{P}{a})$的含义是$a$能代表的数,那方程里为什么要再$\times a$呢?辣鸡博主不是很明白。
C.
设特殊加速器的使用次数为$x$,总费用为$y$,那么$y$关于$x$的函数显然是单谷的,所以可以三分。
考虑如何在已知$x$的情况下快速求出费用。可以对每个点预处理包含它的区间的最右端点,这个直接开个数组对每个$l_i$标记一下就行。
每次计算的时候用差分的思想实现区间减法,不过直接暴力循环似乎也可过??
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N=1e5+5;
typedef long long ll;
int n,m,t,a[N],s[N],L[N],R[N],rm[N],maxh,p[N],v[N];
ll ans=1e15;
ll cacl(int x)
{
for(int i=1;i<=n;i++)
p[i]=max(0,a[i]-x);
ll res=1LL*t*x;
int yet=0;
for(int i=1;i<=n;i++)
{
yet-=v[i];v[i]=0;
p[i]=max(0,p[i]-yet);
res+=p[i];
yet+=p[i];
v[rm[i]+1]+=p[i];
}
return res;
} int main()
{
//freopen("9.in","r",stdin);
n=read();m=read();t=read();
for(int i=1;i<=n;i++)
a[i]=read(),maxh=max(maxh,a[i]);
for(int i=1;i<=m;i++)
{
L[i]=read();R[i]=read();
s[L[i]]=max(s[L[i]],R[i]);
}
int now=0;
for(int i=1;i<=n;i++)
{
if(s[i])now=max(now,s[i]);
rm[i]=now;
if(now==i)now=0;
}
int l=0,r=maxh;
for(int i=1;i<=n;i++)
if(!rm[i])l=max(l,a[i]);
//cout<<l<<endl;
while(l<=r)
{
int mid1=l+(r-l)/3,mid2=l+(r-l)*2/3;
ll val1=cacl(mid1),val2=cacl(mid2);
if(val1>=val2)l=mid1+1,ans=min(ans,val2);
else r=mid2-1,ans=min(ans,val1); }
cout<<ans<<endl;
return 0;
}
D.
暴力的基础上加个剪枝就可以。考虑极限情况,如果当前的$gcd$乘上$(n-i+1)$都没法更新ans($i$为枚举的左端点),那么直接break即可。
实测卡不掉。zkt巨巨证了复杂度,可以做到$O(n\ log\ n)$。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,a[100005],st[100005][21],lg[100005]={-1};
ll ans;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
} int gcd(int x,int y)
{
if(!y)return x;
return gcd(y,x%y);
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
a[i]=read();
//ini();
for(int i=1;i<=n;i++)
{
int now=a[i];
for(int j=i;j<=n;j++)
{
now=gcd(now,a[j]);
if(now==1){ans=max(ans,1LL*(n-i+1));break;}
if(1LL*now*(n-i+1)<ans)break;
ans=max(ans,1LL*(j-i+1)*now);
} } cout<<ans<<endl;
return 0;
}
E.
贪心搜索乱搞水过了。严谨的贪心抽时间补。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n;
ll a[N][3],ans=1e15;
double tim;
void dfs(int x,ll rmax,ll rmin,ll bmax,ll bmin)
{
if(x>1&&(rmax-rmin)*(bmax-bmin)>ans)return ;
if(x>n)
{
ans=min(ans,(rmax-rmin)*(bmax-bmin));
if((clock()-tim)/1e6>=1.5)printf("%lld\n",ans),exit(0);
return ;
}
dfs(x+1,max(rmax,a[x][0]),min(rmin,a[x][0]),max(bmax,a[x][1]),min(bmin,a[x][1]));
dfs(x+1,max(rmax,a[x][1]),min(rmin,a[x][1]),max(bmax,a[x][0]),min(bmin,a[x][0]));
return ;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
for(int j=0;j<=1;j++)
scanf("%lld",&a[i][j]);
if(a[i][0]<a[i][1])swap(a[i][0],a[i][1]);
}
tim=clock();
dfs(1,0,1e15,0,1e15);
cout<<ans<<endl;
return 0;
}
F.
线段树优化dp。
状态定义有些不好想:$f[i][j]$表示进行到第$i$次操作,一个指针在$pos[i]$(题目里给的),另一个在$j$时的最小费用。
$f[i][j]=\min (f[i-1][j]+|p_i-p_{i-1}|)$
$f[i][p[i-1]]=\min (f[i-1][j]+|p_i-j|)$
暴力转移是$O(n^2)$的。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N=1e5+5;
int n,q,p1,p2;
int pos[N];
ll ans=1e15,dp[2005][2005];
int abss(int x)
{
return x>0?x:-x;
}
int main()
{
n=read();q=read();p1=read();p2=read();
for(int i=1;i<=q;i++)pos[i]=read();
memset(dp,0x3f,sizeof(dp));
dp[1][p1]=abss(pos[1]-p2),dp[1][p2]=abss(pos[1]-p1);
for(int i=2;i<=q;i++)
{
for(int j=1;j<=n;j++)
dp[i][pos[i-1]]=min(dp[i-1][j]+1LL*abss(pos[i]-j),dp[i][pos[i-1]]),
dp[i][j]=min(dp[i-1][j]+1LL*abss(pos[i]-pos[i-1]),dp[i][j]); } for(int i=1;i<=n;i++)
ans=min(ans,dp[q][i]);
cout<<ans<<endl;
return 0;
}
我们注意到转移1可以用线段树区间加实现,对于转移2先拆绝对值,维护$f[i][j]+j$和$f[i][j]-j$的最小值即可。
用到的操作:区间修改,区间查询,单点修改。
注意单点修改要放在区间加后面啊!
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef long long ll;
const ll inf=0x3f3f3f3f3f3f3f3f;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n,q,p1,p2;
int pos[N];
ll dp[N];
int abss(int x)
{
return x>0?x:-x;
}
#define ls(k) (k)<<1
#define rs(k) (k)<<1|1
ll a[N<<2],b[N<<2],lz[N<<2];
void up(int k)
{
a[k]=min(a[ls(k)],a[rs(k)]);
b[k]=min(b[ls(k)],b[rs(k)]);
}
void down(int k,int l,int r)
{
int mid=l+r>>1;
lz[ls(k)]+=lz[k];
a[ls(k)]+=lz[k];b[ls(k)]+=lz[k];
lz[rs(k)]+=lz[k];
a[rs(k)]+=lz[k];b[rs(k)]+=lz[k];
lz[k]=0;
}
void build(int k,int l,int r)
{
if(l==r)
{
if(l==p1)a[k]=abss(pos[1]-p2)+p1,b[k]=abss(pos[1]-p2)-p1;
else if(l==p2)a[k]=abss(pos[1]-p1)+p2,b[k]=abss(pos[1]-p1)-p2;
else a[k]=b[k]=inf;
return ;
}
int mid=l+r>>1;
build(ls(k),l,mid);
build(rs(k),mid+1,r);
up(k);
}
void add(int k,int l,int r,int L,int R,ll val)
{
if(l>r)return ;
if(L<=l&&R>=r)
{
a[k]+=val;b[k]+=val;
lz[k]+=val;
return ;
}
if(lz[k])down(k,l,r);
int mid=l+r>>1;
if(L<=mid)add(ls(k),l,mid,L,R,val);
if(R<mid)add(rs(k),mid+1,r,L,R,val);
up(k);
}
void update(int k,int l,int r,int pos,ll val,int op)
{
if(l==r)
{
if(!op)a[k]=min(a[k],val);
else b[k]=min(b[k],val);
return ;
}
if(lz[k])down(k,l,r);
int mid=l+r>>1;
if(pos<=mid)update(ls(k),l,mid,pos,val,op);
else update(rs(k),mid+1,r,pos,val,op);
up(k);
} ll qmin(int k,int l,int r,int L,int R,int op)
{
if(L<=l&&R>=r)return op?b[k]:a[k];
if(lz[k])down(k,l,r);
int mid=l+r>>1;
ll res=inf;
if(L<=mid)res=min(res,qmin(ls(k),l,mid,L,R,op));
if(R>mid)res=min(res,qmin(rs(k),mid+1,r,L,R,op));
return res;
}
ll getans(int k,int l,int r)
{
if(l==r)return a[k]-l;
int mid=l+r>>1;
if(lz[k])down(k,l,r);
return min(getans(ls(k),l,mid),getans(rs(k),mid+1,r));
} int main()
{
n=read();q=read();p1=read();p2=read();
for(int i=1;i<=q;i++)pos[i]=read();
build(1,1,n);
for(int i=2;i<=q;i++)
{
ll val1=qmin(1,1,n,pos[i],n,0)-pos[i],val2=qmin(1,1,n,1,pos[i],1)+pos[i];
add(1,1,n,1,n,abss(pos[i]-pos[i-1])); update(1,1,n,pos[i-1],min(val1,val2)+pos[i-1],0);
update(1,1,n,pos[i-1],min(val1,val2)-pos[i-1],1);
}
cout<<getans(1,1,n)<<endl;
return 0;
}
[CSP模拟测试43、44]题解的更多相关文章
- [CSP-S模拟测试50]反思+题解
??大部分人都觉得T3是道不可做题去刚T1T2了,于是我就侥幸苟到了前面? 这场考试比较成功的就是快速水掉了T1T2的部分分,1h拿到88分起码为之后硬肝T3上了保险(赛后发现就算T3爆零也能rank ...
- csp-c模拟测试43「A·B·C」
B 题解 $f[i][(gcd(prime[j]*prime[k]\%P,P))]=\sum\limits_{k=1}^{k<=num} f[i-1][k]*phi(\frac{P}{prime ...
- [考试反思]0914csp-s模拟测试43:破绽
T1会正解.爆int了,代码里一大堆long long但是有一个地方落了.-70分. 离考试结束还有19秒的时候发现手模样例爆负数了,没来得及改. T2没想.打暴力了.然而实际很好想...早读5分钟就 ...
- [CSP-S模拟测试48]反思+题解
状态很垃圾的一场考试.感觉“这么多分就够了”的心态很是在给自己拖后腿. 打开题面,第一页赫然写着:$Claris' Contest$. 吓得我差点手一抖关掉.不过后来想想似乎强到变态的人出的题都不是很 ...
- [CSP-S模拟测试47]反思+题解
打开题面,T3似乎被换过了.(那我就更有理由直接弃掉了) T1是我最害怕的乱搞题,赶紧扔了看T2.发现是个sb板子?雨天的尾巴弱化版? 然而线段树合并早忘干净了(最近几道可以线段树合并的题都是用别的方 ...
- [NOIP模拟测试37]反思+题解
一定要分析清楚复杂度再打!!!窝再也不要花2h20min用暴力对拍暴力啦!!! 雨露均沾(滑稽),尽量避免孤注一掷.先把暴力分拿全再回来刚正解. 即使剩下的时间不多了也优先考虑认真读题+打暴力而非乱搞 ...
- [NOIP模拟测试34]反思+题解
不要陷入思维定势,如果长时间没有突破就要考虑更改大方向. 不要把简单问题复杂化. 做完的题就先放下,不管能拿多少分.不能过一段时间就回来调一下. $Solutions:$ A.次芝麻 因为$n+m$始 ...
- [NOIP模拟测试32]反思+题解
又考挂了QAQ 总rank直接滑出前20 晚上考试脑子还算比较清醒,可惜都用来xjb乱想错误思路了. T1一眼推柿子,然而并没有头绪所以先码了个暴力.然后…… 一个垃圾暴力我调了1h,大概解决了两位数 ...
- [NOIP模拟测试7]visit 题解(组合数学+CRT+Lucas定理)
Orz 因为有T的限制,所以不难搞出来一个$O(T^3)$的暴力dp 但我没试 据说有30分? 正解的话显然是组合数学啦 首先$n,m$可能为负,但这并没有影响, 我们可以都把它搞成正的 即都看作向右 ...
随机推荐
- angularjs radio 默认选中
添加ng-model后checked="checked"失效,可见angularjs也不好,会失效html标准属性 解决:添加ng-checked="1" &l ...
- mysql创建,添加主键
primary key 1.最简单的: CREATE TABLE t1( id int not null, name char(20)); 2.带主键的: a:CREATE TABLE t1( id ...
- hdu 5517 Triple
题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=5517 ------------------------------------------------ ...
- docker 应用
在ubuntu安装docker 编写Dockerfile (用来操作容器) FROM java:8 #获取java官方镜像 jdk版本为1.8 VOLUME /tmp # 数据存储目录,容器退出后数据 ...
- javascript标签放置位置
首先:放置位置哪里都能放 但是js代码很有可能不起作用:例如:往id为span的标签中定时插入数字 var time=document.getElementById("span") ...
- CF560补题
D题:用来对比的vector<long long> b不能被初始化大小成n,因为a里面有n个因子,但是这是可能存在遗漏情况的.如果刚好是遇到实际因子远多于n,那么就会在运行过程中出错. 还 ...
- 怒转一波,此人整理的Flink特别好
Apache Flink:特性.概念.组件栈.架构及原理分析 Apache Flink是一个面向分布式数据流处理和批量数据处理的开源计算平台,它能够基于同一个Flink运行时(Flink Runtim ...
- Python:我可以使用类变量作为线程锁吗?
我正在考虑使用类变量作为线程锁,因为我不想在全局变量中定义锁并且还想防止死锁.这实际上有用吗?例: import threading class A(object): lock = threading ...
- runtime之归档和解档
IOS开发之NSCoding协议(使用runtime)近期学习IOS的runtime库,然后看到之前写的NSCoding协议有点复杂,如果属性少还好,如果100多个属性,则会显得麻烦.下面使用常规方式 ...
- 第4篇创建harbor私有镜像库
一.部署准备: 1.准备harbor软件包 在部署节点上: 2.挂载一个磁盘,专门存储harbor镜像和文件 3.进入到/etc/docker/harbor/目 ...