ABC340
\(\huge{C}\)
link
首先,考虑暴力,用一个堆,存所有数,每次拿出最大的数,拆开加入堆,计入答案,直到最大的\(\le1\),时间复杂度\(O(\text{不能过})\)。
考虑想求出\(n\),要什么。
求\(n\)一定是第一次把\(n\)拆成\(\lfloor{\frac{n}{2}}\rfloor\)和\(\lceil{\frac{n}{2}}\rceil\),答案加上\(n\),之后再继续拆\(\lfloor{\frac{n}{2}}\rfloor\)和\(\lceil{\frac{n}{2}}\rceil\),再继续拆……简单考虑一下即可得,\(n\)的答案就是\(n\)+\(\lfloor{\frac{n}{2}}\rfloor\)和\(\lceil{\frac{n}{2}}\rceil\)的答案。
这时,可以递归,加一个记忆化,时间复杂度\(O(\text{能过})\)。
特殊情况
- \(n=2\)时,答案为\(2\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
map<int,int> mp;
int dfs(int x){
if(x == 2) return 2;
if(x == 1) return 0;
if(mp.find(x) != mp.end()) return mp[x];
mp[x] = dfs(x/2)+dfs((x+1)/2)+x;
return mp[x];
}
signed main(){
cin >> n;
cout << dfs(n);
return 0;
}
\(\huge{D}\)
link
每个点可以到\(i+1\)和\(x_i\),考虑\(i\)向\(i+1\)和\(x_i\)连边,跑\(Dijkstra\)即可。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int>
using namespace std;
int n;
int a[200005],b[200005],x[200005];
vector<pair<int,int> > ed[200005];
int f[200005];
void dijkstra(int s){
priority_queue<PII,vector<PII>,greater<PII> > q;
q.push({0,s});
for(int i = 1;i <= n;++ i)
f[i] = 1e15;
f[s] = 0;
while(!q.empty()){
int t = q.top().first,x = q.top().second;
q.pop();
if(t > f[x]) continue;
for(int i = 0;i < ed[x].size();++ i){
int y = ed[x][i].first;
int w = ed[x][i].second;
if(f[y] > f[x]+w){
f[y] = f[x]+w;
q.push({f[y],y});
}
}
}
}
signed main(){
cin >> n;
for(int i = 1;i < n;++ i){
cin >> a[i] >> b[i] >> x[i];
ed[i].push_back({i+1,a[i]});
ed[i].push_back({x[i],b[i]});
}
dijkstra(1);
cout << f[n];
return 0;
}
\(\huge{E}\)
link
最开始我看到每个加1,于是考虑到了差分,可是差分无法随时找出\(a_{b_i}\),无法用。
于是,我们需要一个即能区间修改值,又能随时查询某个点的值得数据结构——线段树。
每次输入了\(b_i\)(后面用\(x\)),找出\(x\)的值(\(y\)),减去,然后用线段树把要加\(1\)的区间加上\(1\)。
这里要加\(1\)的区间可能\(\ge{n}\),先整个加\(y/n\),在把剩下的不够\(n\)的单独加上。
把不够\(n\)的单独加上的过程中,可能会是\(x+1\)到\(n\)和\(1\)到\(x+y-n\),也可能会是\(x+1\)直接到\(x+y\),如果是前者\(x+y\)要大于\(n\)。
前者如果\(x+1\)大于\(n\)了,就没有\(x+1\)到\(n\)这一段了。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int q[200005];
struct nd{
int l,r;
int ls,rs;
int val;
int lazy;
}a[200005*4+1];
int le(int x){
return x<<1;
}
int re(int x){
return (x<<1)+1;
}
void pd(int x){
int le = a[x].ls;
int re = a[x].rs;
int k = a[x].lazy;
a[le].lazy += a[x].lazy;
a[le].val += k*(a[le].r-a[le].l+1);
a[re].lazy += a[x].lazy;
a[re].val += k*(a[re].r-a[re].l+1);
a[x].lazy = 0;
}
void ud(int x){
pd(x);
a[x].val = a[a[x].ls].val+a[a[x].rs].val;
}
int build(int x,int l,int r){
a[x].l = l,a[x].r = r;
if(l == r){
a[x].val = q[l];
return q[l];
}
int mid = (l+r)>>1;
int ll = build(le(x),l,mid);
int rr = build(re(x),mid+1,r);
a[x].ls = le(x);
a[x].rs = re(x);
ud(x);
return a[x].val;
}
void geg(int xl,int xr,int k,int x){
if(xl <= a[x].l&&a[x].r <= xr){
a[x].lazy += k;
a[x].val += k*(a[x].r-a[x].l+1);
return;
}
pd(x);
int mid = (a[x].l+a[x].r)>>1;
if(xl <= mid) geg(xl,xr,k,le(x));
if(xr > mid) geg(xl,xr,k,re(x));
ud(x);
}
int sc(int xl,int xr,int x){
if(xl <= a[x].l&&a[x].r <= xr){
return a[x].val;
}
pd(x);
int mid = (a[x].l+a[x].r)>>1;
int rt = 0;
if(xl <= mid) rt += sc(xl,xr,le(x));
if(xr > mid) rt += sc(xl,xr,re(x));
return rt;
}
signed main(){
cin >> n >> m;
for(int i = 1;i <= n;++ i)
cin >> q[i];
build(1,1,n);
for(int i = 1;i <= m;++ i){
int x;
cin >> x;
x++;
int y = sc(x,x,1);
if(y == 0) continue;
geg(x,x,-1*y,1);
int z = y/n;
y %= n;
geg(1,n,z,1);
if(y == 0) continue;
if(x+y > n){
if(x+1 <= n)
geg(x+1,n,1,1);
geg(1,x+y-n,1,1);
}
else geg(x+1,x+y,1,1);
}
for(int i = 1;i <= n;++ i)
cout << sc(i,i,1) << " ";
return 0;
}
\(\huge{F}\)
link
看一下这幅图。
\(l=\sqrt{A^2+B^2}\)且\(h \cdot l=2\)
\(l\)所在的这条直线的表达式为\(y=kx\),即\(B=kA\)
解得\(k=\frac {B}{A}\)。
两条红线的表达式与\(l\)所在的直线的\(k\)相同,都是\(\frac {B}{A}\)。
那\(b\)呢?
可以看出,绿色三角形与黄色三角形是相似三角形(相同颜色的边是对应边),可得
\(\frac {h}{A}=\frac {b}{L}\rightarrow\)
\(h\cdot L=A\cdot b\rightarrow\)
\(b=\frac{h\cdot L}{A}\rightarrow\)
\(b=\frac{2}{A}\)
所以两条红线的表达式为
\(y=\frac{B}{A}x\pm\frac{2}{A}\)
即
\(Ay=Bx\pm2\rightarrow\)
\(Bx-Ay=\pm2\)
看到这里,我们想到了\(exgcd\)。
\(ax+by=gcd(a,b)\)
那么这里\(a\)是\(B\),\(b\)是\(a\)。
我们用\(exgcd\)求出一个答案,同时求出\(gcd(A,B)\)。
分三种情况
- \(gcd(A,B)>2\),无解;
- \(gcd(A,B)=2\),求出的\(x\)和\(y\)即为解。
- \(gcd(A,B)=1\),求出的\(x\)和\(y\)乘上\(2\)即为解。
注意,在后两种情况中,由于式子是\(Bx-Ay=\pm2\),所以\(y\)要乘上\(-1\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a,b;
//用exgcd求解和gcd
int exgcd(int &x,int &y,int a,int b){
if(!b){
//如果b==0 gcd(a,b)=a
x = 1;
y = 0;
return a;
}
//递归
int tt = exgcd(x,y,b,a%b);
int t = x;
//返回求解
x = y;
y = t-a/b*y;
return tt;
}
signed main(){
cin >> a >> b;
int x,y;
int g = exgcd(x,y,b,a);
//如果gcd(a,b)>2无解
if(abs(g) > 2){
cout << -1;
return 0;
}
//如果gcd(a,b)=1,解需要乘2
if(abs(g) == 1){
x *= 2;
y *= 2;
}
//由于式子中间符号是-,所以y要乘-1
cout << x << " " << y*-1;
return 0;
}