ABC340

C
link

首先,考虑暴力,用一个堆,存所有数,每次拿出最大的数,拆开加入堆,计入答案,直到最大的1,时间复杂度O(不能过)
考虑想求出n,要什么。
n一定是第一次把n拆成n2n2,答案加上n,之后再继续拆n2n2,再继续拆……简单考虑一下即可得,n的答案就是n+n2n2的答案。
这时,可以递归,加一个记忆化,时间复杂度O(能过)
特殊情况

  • 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;
	
} 

D
link

每个点可以到i+1xi,考虑ii+1xi连边,跑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;
	
}

E
link

最开始我看到每个加1,于是考虑到了差分,可是差分无法随时找出abi,无法用。
于是,我们需要一个即能区间修改值,又能随时查询某个点的值得数据结构——线段树。
每次输入了bi(后面用x),找出x的值(y),减去,然后用线段树把要加1的区间加上1
这里要加1的区间可能n,先整个加y/n,在把剩下的不够n的单独加上。
把不够n的单独加上的过程中,可能会是x+1n1x+yn,也可能会是x+1直接到x+y,如果是前者x+y要大于n
前者如果x+1大于n了,就没有x+1n这一段了。

点击查看代码
#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;
	
}

F
link

看一下这幅图。
l=A2+B2hl=2
l所在的这条直线的表达式为y=kx,即B=kA
解得k=BA
两条红线的表达式与l所在的直线的k相同,都是BA
b呢?
可以看出,绿色三角形与黄色三角形是相似三角形(相同颜色的边是对应边),可得
hA=bL
hL=Ab
b=hLA
b=2A
所以两条红线的表达式为
y=BAx±2A

Ay=Bx±2
BxAy=±2
看到这里,我们想到了exgcd
ax+by=gcd(a,b)
那么这里aBba
我们用exgcd求出一个答案,同时求出gcd(A,B)
分三种情况

  • gcd(A,B)>2,无解;
  • gcd(A,B)=2,求出的xy即为解。
  • gcd(A,B)=1,求出的xy乘上2即为解。

注意,在后两种情况中,由于式子是BxAy=±2,所以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 @   不认命,就是哪吒的命!  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示