n n n 人在坐标轴上,第 i i i 人在 X i X_i Xi 点,到 X 0 X_0 X0 时间为 ∣ X 0 − X i ∣ |X_0-X_i| ∣X0−Xi∣ ,第 i i i 人的准备时间为 T i T_i Ti ,总时间是 T i + ∣ X 0 − X i ∣ T_i+|X_0-X_i| Ti+∣X0−Xi∣。
在坐标轴上找一个点使所有人到这个点的总时间最短。
误差小于 1 0 − 6 10^{-6} 10−6。
既然给误差值了自然想到 二分 (手动滑稽)
这道题需要二分时间:
二分位置,无法证明其地点与时间的单调性,舍弃;
二分时间,取所有人的该时间内可到地点的交集;
由题知,该地点唯一;
所以最后得到的交集一定为一个长度小于 1 e − 6 1e-6 1e−6 的区间,达到精度后输出其端点即可。
#include
#include
using namespace std;
const double EPS=1e-7;//多二分一位更放心
int T, num,pos[100001],t[100001];
double l,r,ans;
bool check(double times){
double ll=-1e9,rr=1e9;
for(int i=1;i<=num;i++){
double extra=times-t[i];
if(extra<0)return 0;
double posl=pos[i]-extra,posr=pos[i]+extra;
ll=max(ll,posl),rr=min(rr,posr);//不断取交集
}
//[ll,rr]即所有人可以到达的地点区间
if(rr-ll>=EPS)ans=ll;//check函数不断更新ans
return rr-ll>=EPS;
}
void solve(){
scanf("%d",&num);
l=r=1e9;
for(int i=1;i<=num;i++)scanf("%d",&pos[i]);
for(int i=1;i<=num;i++)scanf("%d",&t[i]),l=min(l,(double)t[i]);//缩小二分范围,r不知道上限,写无穷大就行
while(r-l>=EPS){
double mid=(l+r)/2;
if(check(mid))r=mid;else l=mid;
}
printf("%lf\n",ans);//这里要用printf,改成cout就会自动四舍五入,过不了。
}
int main(){
for(scanf("%d",&T);T--;)solve();
}
具体思路看注释,也可以自己手推试试
#include
#include
#define yes puts("YES")
#define no puts("NO")
#define ll long long//数据范围
using namespace std;
ll t,a,b,x;
void solve(ll a,ll b){
if(a<b)swap(a,b);//通过手模数据发现由于操作具有对称性,只有a,b的数值影响结果,与a,b位置无关,
//把大数字放前,可以去掉绝对值,方便计算。
if(!b||x>a){no;return;}//存在两种情况不符合条件,a,b 存在 0 或 x 大于 a,b 手推易懂;
if(a==x||b==x){yes;return;}//存在两种符合条件的,即题目意思。
if(a%b==x%b){yes;return;}
//手模数据,发现终会推到 (a-k*b,b) 这一步去除 k*b (k>=0) 即 a,x 存在公约数 b ,则多次操作一定能凑出x;
solve(a%b,b);//重复操作
}
int main(){
for(cin>>t;t--;)cin>>a>>b>>x,solve(a,b);//由于需要递归操作,读入写在函数外部
}
#include
using namespace std;
#define int long long
int a[100001],n,m,i,x,ans;
main() {
cin >> n >> m;
for (i = 1; i <= n; ++i)
cin >> a[i];
for (i = 1; i <= n; ++i)
ans += (a[i] != a[i + 1]) * (n - (i + 1) + 1) * i;//初始贡献
while (m--) {
cin >> i >> x;
ans -= (a[i]!=a[i - 1]) * (n - i + 1) * (i - 1);//减去原数字的贡献
ans -= (a[i]!=a[i + 1]) * (n - (i + 1) + 1) * i;
a[i] = x;
ans += (a[i]!=a[i - 1]) * (n - i + 1) * (i - 1);//加上修改后数字的贡献
ans += (a[i]!=a[i + 1]) * (n - (i + 1) + 1) * i;
cout << ans + n * (n + 1) / 2 << '\n';//算上初始贡献
}
}
你有任意个魔法宝石,每个魔法宝石可以分解为m个普通宝石,每个魔法宝石和普通宝石占1单位空间。现在你有一个长度为n的空间,问有多少种方案可以填满所有空间。
矩阵快速幂。
#include
using namespace std;
typedef long long ll;
const int N = 200000 + 10;
const ll mod = 1e9 + 7;
ll b[N], vis[N], in[N], num[N], a[N], t, n, m, y, k, x;
int cnt, sum, flag, f[N];
vector<int> v[N], ans;
struct mat{
ll a[105][105];
};
mat mul(mat a, mat b){
mat ans;
for (int i = 1; i <= m; i++){
for (int j = 1; j <= m; j++){
ans.a[i][j] = 0;
for (int k = 1; k <= m; k++){
ans.a[i][j] += a.a[i][k] * b.a[k][j];
if (ans.a[i][j] > mod)
ans.a[i][j] %= mod;
}
}
}
return ans;
}
mat ksm(mat a, ll b){
mat ans;
for (int i = 1; i <= m; i++)
ans.a[i][i] = 1;
while(b){
if(b & 1)ans = mul(ans, a);
b >>= 1;
a = mul(a, a);
}
return ans;
}
int main(){
cin >> n >> m;
if (n < m)return cout << 1 << endl, 0;
mat tr;
for (int i = 1; i < m; i++)tr.a[i + 1][i] = 1;
tr.a[1][1] = tr.a[1][m] = 1;
mat ans;
for (int i = 2; i <= m; i++)ans.a[i][1] = 1; //行列关系不要搞错
ans.a[1][1] = 2; // 1,1是dp[m]的值为2
ans = mul(ksm(tr, n - m), ans); //这里是用tr乘ans,顺序不能搞反!!!
cout << ans.a[1][1] << endl;
}