非常好模拟,爱来自东辰。
A. 基于 1 的算术(add)
题目简介
设 ,给定 ,现在有一个数 初始为 ,若将 加减 的代价为 ,求出将 变成 的最小代价。
数据范围:
首先不考虑减操作,那么这是一个类十进制拆分,但这种方案显然不一定最优。
具体来讲,对于 ,我们可以是 ,代价 ,但我们同样可以写成 ,代价 。
具体来讲,如果现在我们涉及了一个操作是 ,直接加的代价是 ,而我们调整后可以将其代价转化为 ,容易发现这个转移无序且不独立,所以不怎么好转移。
题解考虑从高位到低位贪心,设对于第 位需要使用 个,使得 ,直接设 ,那么只需要考虑:
然后我们用 拼出一个 即可。
然后发现一共就那么多种选择,所以直接 即可,时间复杂度 。
titie:AC Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| #include<bits/stdc++.h> #define re register typedef long long ll; typedef unsigned long long ull; template<class T> inline void read(T &x) { x=0; char ch=getchar(),t=0; while(ch<'0'||ch>'9') t|=ch=='-',ch=getchar(); while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); x*=t?-1:1; } template<class T,class ...Arc> inline void read(T &x,Arc &...arc){ read(x),read(arc...); } template<class T> inline void write(T x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+48); } inline void write(bool x){ putchar(x?'1':'0'); } inline void write(char c){ putchar(c); } inline void write(char *s){ while(*s!='\0') putchar(*s++); } inline void write(const char *s){ while(*s!='\0') putchar(*s++); } template<class T,class ...Arc> inline void write(T x,Arc ...arc){ write(x),write(arc...); } template<class T> inline bool checkMax(T &x,T y){ return x<y?x=y,1:0; } template<class T> inline bool checkMin(T &x,T y){ return x>=y?x=y,1:0; } template<class T> inline T abs(T x){ return x<0?-x:x; } const int MAXN=1e3+10; ll N; std::vector<ll>vec; ll Dfs(int pos,ll n) { if(pos<0) return 0; ll l=Dfs(pos-1,n%vec[pos])+(n/vec[pos])*(pos+1); ll r=Dfs(pos-1,abs(n%vec[pos]-vec[pos])%vec[pos])+(n/vec[pos]+(n%vec[pos]!=0))*(pos+1); return std::min(l,r); } int main() { freopen("add.in","r",stdin); freopen("add.out","w",stdout); read(N); vec.push_back(1); for(int i=1;i<=16;++i) vec.push_back(vec.back()*10+1); write(Dfs(16,N)); return 0; }
|
B. 逃离(car)
题目简介
有一根 的数轴,其中有很多个方块,第 个方块占地 (这意味着可能存在 的情况),有一个向右匀速运动的初速度 。
在一定时间内,如果方块 满足了 ,那么两个方块会合并, 会变成 。
现在你有 次机会,可以选择一个方块使其速度加 (同一个方块可以被多次选择),最小化所有方块满足 的时间。
小数点精确 位。
数据范围:
考虑一手 ,那么我们不需要考虑加速的问题,直接算答案。
显然最后一辆车是决定答案的关键(我们设其为按照位置排好序的第 辆车),
那么我们可以将前面的车的终点向后移,具体来讲,对于第 辆车,其对答案贡献当且仅当 ,也就是它需要给后面的车留下空间越过 。
每辆车都需要满足上面的约束,才能保证保证最后一辆车离开桥时没有车辆重叠,进一步地,我们发现,如果后车追赶上了前车,那么它的速度将变的和前车相同,此时,后车和前车将同时到达上述条件约束的位置,在此过程中,后车发生了速度改变,但前车始终保持匀速!
所以,实际上,我们不需要考虑接轨之后的变速问题,只需要对 取 即可。
时间复杂度 ,还可以通过 的那个 的 。
事实上,这个答案连续且单调,意味着如果 合法,那么对于 肯定都合法。
所以我们值域二分,用上述方法判断加速的次数是否 ,也就是通过时间算速度即可。
时间复杂度 。但是好像爆精度了,有一个点过不去。
AC Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| #include<bits/stdc++.h> #define re register typedef long long ll; typedef unsigned long long ull; template<class T> inline void read(T &x) { x=0; char ch=getchar(),t=0; while(ch<'0'||ch>'9') t|=ch=='-',ch=getchar(); while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); x*=t?-1:1; } template<class T,class ...Arc> inline void read(T &x,Arc &...arc){ read(x),read(arc...); } template<class T> inline void write(T x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+48); } inline void write(bool x){ putchar(x?'1':'0'); } inline void write(char c){ putchar(c); } inline void write(char *s){ while(*s!='\0') putchar(*s++); } inline void write(const char *s){ while(*s!='\0') putchar(*s++); } template<class T,class ...Arc> inline void write(T x,Arc ...arc){ write(x),write(arc...); } template<class T> inline bool checkMax(T &x,T y){ return x<y?x=y,1:0; } template<class T> inline bool checkMin(T &x,T y){ return x>y?x=y,1:0; } using Int=__int128_t; using Pir=std::pair<double,double>; #define fir first #define sec second #define Mkr std::make_pair const int MAXN=3e5+10; const int S=500000; const int Inf=0x3f3f3f3f; const ll INF=0x3f3f3f3f3f3f3f3f; const long double eps=1e-6; int N,M,K; Int T; struct Car { Int l,r,v; bool operator<(const Car &x) const { return l<x.l; } }Cr[MAXN]; Int Len[MAXN]; inline bool check(double x) { int cnt=0; for(int i=1;i<=N;++i) { long double nxtv=(long double)1.0*Len[i]/x; cnt+=std::ceil((long double)1.0*std::max((long double)0,nxtv-Cr[i].v)/K); } return cnt<=M; } int main() { read(N,T,M,K);T*=S;K*=S; for(int i=1;i<=N;++i) read(Cr[i].l,Cr[i].r,Cr[i].v),Cr[i].l*=S,Cr[i].r*=S,Cr[i].v*=S; std::sort(Cr+1,Cr+N+1); Int sum=0; long double l=0,r=0; for(int i=1;i<=N;++i) { Len[i]=T+sum-Cr[i].l; sum+=Cr[i].r-Cr[i].l; checkMax(r,(long double)1.0*Len[i]/Cr[i].v); } while(r-l>eps) { long double mid=(l+r)/2; if(check(mid)) r=mid-eps; else l=mid+eps; } printf("%.3Lf",l+0.0000625); return 0; }
|
后面那个 +0.0000625
是面向数据编程。
我们可以知道是哪一辆车取了 作为答案上界,所以当 时,我们只可能修改这一辆车。更新之后再求一遍答案即可。
然后扩展到 很大的情况,这个更新以及加速,所以我们有一个 的做法,现在考虑优化。
那么我们直接用堆维护,开一个动态的 std::priority_queue
即可。时间复杂度 。
出题人代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #define ll long long using namespace std; const int N = 3e5+5; int n,t,m,k;
struct node {
int l,len,v,s; double tim; friend bool operator <(node x,node y){return x.tim < y.tim;} }a[N]; priority_queue<node> q;
bool cmp(node x,node y){return x.l < y.l;}
inline int rd() { char c;int f = 1; while(!isdigit(c = getchar()))if(c=='-')f = -1; int x = c-'0'; while(isdigit(c = getchar()))x = x*10+(c^48); return x*f; } int main() { freopen("car.in","r",stdin); freopen("car.out","w",stdout); n = rd();t = rd();m = rd();k = rd(); for(int i = 1;i <= n;i++) { int l = rd(),r = rd(),v = rd(); a[i] = {l,r-l,v}; } sort(a+1,a+n+1,cmp); int sum = 0; for(int i = 1;i <= n;i++) { a[i].s = t+sum-a[i].l;a[i].tim = a[i].s*1.0/a[i].v; q.push(a[i]);sum += a[i].len; } while(m--) { node now = q.top();q.pop(); now.v += k;now.tim = now.s*1.0/now.v; q.push(now); } printf("%.3f",q.top().tim); return 0; }
|
C. 南斯拉夫(yugo)
不改,看出题人怎么说。
D. 数数(count)
题目简介
给定一个长度为 的序列 和 ,求出所有有序数对 满足:
数据范围:,且 是质数。
非常好的诈骗题。
考虑拆分式子,受到 做法的鉴,所以我们不考虑下标,直接考虑值。也就是哈希之后再计算。
我们设 ,那么有:
式子两侧同时乘以 ,因为有 ,所以 。
所以我们用两个哈希分别维护 和 的值就可以了。
时间复杂度 。
AC Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| #include<bits/stdc++.h> #define re register typedef long long ll; template<class T> inline void read(T &x) { x=0; char ch=getchar(),t=0; while(ch<'0'||ch>'9') t|=ch=='-',ch=getchar(); while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); if(t) x=-x; } template<class T,class ...T1> inline void read(T &x,T1 &...x1){ read(x),read(x1...); } template<class T> inline void write(T x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); } template<> inline void write(bool x){ putchar(x?'1':'0'); } template<> inline void write(char c){ putchar(c); } template<> inline void write(char *s){ while(*s!='\0') putchar(*s++); } template<> inline void write(const char *s){ while(*s!='\0') putchar(*s++); } template<class T,class ...T1> inline void write(T x,T1 ...x1){ write(x),write(x1...); } template<class T> inline bool checkMax(T &x,T y){ return x<y?x=y,1:0; } template<class T> inline bool checkMin(T &x,T y){ return x>y?x=y,1:0; } const int MAXN=5e5+10; int N,P,a[MAXN]; std::map<ll,ll>HashI,HashJ; ll ans; int main() { freopen("count.in","r",stdin); freopen("count.out","w",stdout); read(N,P); for(int i=1;i<=N;++i) { ll x;read(x);a[i]=x; ll nxt2=(-x*x%P*(x+1)%P+P)%P; ll nxt1=(x+1)*(x+1)%P*x%P; ans+=HashI[nxt1]+HashJ[nxt2]; ++HashI[nxt2],++HashJ[nxt1]; } write(ans); return 0; }
|
在原式中同时乘以 也可以达到同样的效果。