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;
	
}
posted @ 2024-02-11 10:45  校牌杀手  阅读(38)  评论(0编辑  收藏  举报