首页 > 其他分享 >提高组杂题训练1

提高组杂题训练1

时间:2024-10-12 17:59:41浏览次数:9  
标签:训练 int 提高 ans mid long 组杂题 sum id

A [USACO22DEC] Breakdown P

首先 \(N\le300\) \(k\le8\) 看样子复杂度是个 3 次的东西。一些套路的东西比如删边改加边不说了。这个 \(K\le8\) 很有讲究。

首先,不妨折半一下,算出从 1 经过一半条边到 \(u\) 的最短路径和 \(u\) 到 \(n\) 的最短路径,那么答案就可以 \(\mathcal{O}(n)\) 合并。对于 \(k'\le 4\) ,可以维护两个数组 \(f[u][v]\) 和 \(g[u][v]\),分别表示 \(u\sim v\) 走一条边和走两条边的最短路,维护 \(h[u]\) 数组表示从起始节点到 \(u\) 经过 \(k'\) 条边的最短路径。可以惊人的发现 1 和 2 可以凑出所有 4 以内的情况!!当加入边 \(u, v\) 时,当且仅当 \(u\) 为 \(bg\) 的时候需要枚举所有断点维护 \(h\),所以加边操作均摊 \(\mathcal{O}(n)\)。总复杂度 \(\mathcal{O}(N^3)\)。

#include<bits/stdc++.h>
using namespace std;
#define int long long
constexpr int N = 305;
int n, k, x[N*N], y[N*N], ans[N*N], edge[N][N];
struct XiaoLe{
	int f[N][N], g[N][N], h[N], fk, bg;
	const int& operator [](const int x) const { return h[x]; }
	inline void build(int kf, int gb){
		fk = kf; bg = gb;
		memset(f, 0x3f, sizeof(f));
		if(fk > 1) memset(g, 0x3f, sizeof(g));
		memset(h, 0x3f, sizeof(int)*(n+1));
		if(!fk) h[bg] = 0;
	}
	inline void add(int u, int v, int val){
		if(!fk) return;
		if(fk == 1){
			if(u ^ bg) return;
			h[v] = min(h[v], val);
			return;
		}
		f[u][v] = val;
		for(int i=1; i<=n; ++i){
			g[i][v] = min(g[i][v], f[i][u] + val);
			g[u][i] = min(g[u][i], val + f[v][i]);
		}
		if(fk == 2){
			for(int i=1; i<=n; ++i)
				h[i] = min(h[i], g[bg][i]);
		} else if(fk == 3){
			if(u == bg){
				for(int i=1; i<=n; ++i) for(int j=1; j<=n; ++j)
					h[i] = min(h[i], f[bg][j] + g[j][i]);
			} else{
				for(int i=1; i<=n; ++i){
					h[i] = min({h[i], g[bg][u] + f[u][i], g[bg][v] + f[v][i]});
					h[u] = min(h[u], f[bg][i] + g[i][u]);
					h[v] = min(h[v], f[bg][i] + g[i][v]);
				}
					
			}
		} else{
			if(u == bg){
				for(int i=1; i<=n; ++i) for(int j=1; j<=n; ++j)
					h[i] = min(h[i], g[bg][j] + g[j][i]);
			} else {
				for(int i=1; i<=n; ++i){
					h[i] = min({h[i], g[bg][u] + g[u][i], g[bg][v] + g[v][i]});
					h[u] = min(h[u], g[bg][i] + g[i][u]);
					h[v] = min(h[v], g[bg][i] + g[i][v]);
				}
			}
		}
	}
} m1, mn;
signed main(){
	ios::sync_with_stdio(0), cout.tie(0), cout.tie(0);
	cin>>n>>k; int nn = n * n;
	for(int i=1; i<=n; ++i) for(int j=1; j<=n; ++j)
		cin>>edge[i][j];
	m1.build(k >> 1, 1), mn.build((k + 1) >> 1, n); 
	memset(ans, 0x3f, sizeof(int)*(nn+1));
	for(int i=1; i<=nn; ++i) cin>>x[i]>>y[i];
	for(int i=nn, u, v; i>=1; --i){
		for(int j=1; j<=n; ++j)
			ans[i] = min(ans[i], m1[j] + mn[j]);
		ans[i] = ans[i] > 1e9 ? -1 : ans[i];
		u = x[i], v = y[i];
		m1.add(u, v, edge[u][v]);
		mn.add(v, u, edge[u][v]);
	}
	for(int i=1; i<=nn; ++i) cout<<ans[i]<<'\n';
	return 0;
}

B [USACO22DEC] Making Friends P

按时间模拟,对每个节点维护 set 表示当前节点都和谁是朋友关系,每次删掉一个节点之后,ans 就加上 size,并把新建立的朋友关系加到下一个最先走掉的节点的 set 里。相当于维护一个集合,也可以使用线段树合并。最后 ans -= m。

#include<bits/stdc++.h>
using namespace std;
constexpr int N = 2e5 + 5;
int n, m; long long ans;
set<int> st[N];
int main(){
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin>>n>>m; ans = -m;
	for(int i=1, u, v; i<=m; ++i){
		cin>>u>>v; if(u > v) swap(u, v);
		st[u].insert(v);
	}
	for(int i=1; i<=n; ++i){
		if(st[i].empty()) continue;
		ans += st[i].size();
		int u = *st[i].begin(), v = i;
		st[i].erase(st[i].begin());
		if(st[u].size() < st[v].size()) swap(st[u], st[v]);
		for(int it : st[v]) st[u].insert(it);
	} return cout<<ans<<'\n', 0;
}

C [USACO22DEC] Palindromes P

分析一下回文串的性质,因为只有两种字符,所以很好分析,这两种字符就用 \(0\) \(1\) 代替了,并且只要 \(1\) 满足性质,\(0\) 必然满足,所以只分析 \(1\) 即可。

对于一个串 \([L,R]\) ,它是轴对称的,并且对称中心为一个或者两个 \(1\),不妨枚举所有对称中心并扩展。假如扩展的两个 \(1\) 分别为 \(l\) 和 \(r\),那么这俩关于对称中心的充要条件即为 \(l+r=L+R\),那么移动步数为 \(|L+R-(l+r)|\)。那么每次扩展两个 \(1\),就把它的权值加到树状数组里维护即可。复杂度 \(\mathcal{O}(N^2\log N)\)。

#include<bits/stdc++.h>
using namespace std;
#define int long long
constexpr int N = 7505;
string s; int n, ans, g[N], cnt;
struct BIT{
	#define lb ((i) & (-i))
	int t[N<<1];
	inline void add(int x, int val){
		for(int i=x; i<=n*2; i+=lb) t[i] += val;
	}
	inline int qry(int x){
		int ans = 0;
		for(int i=x; i; i-=lb) ans += t[i];
		return ans;
	}
} t1, t2;
signed main(){
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin>>s; n = s.size();
	for(int i=1; i<=n; ++i) if(s[i-1] == 'G') g[++cnt] = i;
	g[cnt+1] = n+1;
	for(int i=1, x=1, y=1; i<=cnt; ++i, x=i, y=i){
		int sum = 0, nm = 0;
		while(1 <= x && y <= cnt){
			int tot = g[x] + g[y];
			if(x != i) t1.add(tot, tot), t2.add(tot, 1), sum += tot, ++nm;
			for(int l=g[x]; l>g[x-1]; --l) for(int r=g[y]; r<g[y+1]; ++r){
				if((r - l) & 1){ --ans; continue; }
				tot = l + r;
				int val = t1.qry(tot-1), num = t2.qry(tot-1);
				ans += llabs((tot >> 1) - g[i]);
				ans += num * tot - val + sum - val - tot * (nm - num);
			} --x, ++y;
		}
        memset(t1.t, 0, sizeof(int) * (2*n + 1));
        memset(t2.t, 0, sizeof(int) * (2*n + 1));
	}
    for(int i=1, x=1, y=2; i<cnt; ++i, x=i, y=i+1){
        int sum = 0, nm = 0;
        while(1 <= x && y <= cnt){
            int tot = g[x] + g[y];
            t1.add(tot, tot), t2.add(tot, 1), sum += tot, ++nm;
            for(int l=g[x]; l>g[x-1]; --l) for(int r=g[y]; r<g[y+1]; ++r){
                tot = l + r;
                int val = t1.qry(tot-1), num = t2.qry(tot-1);
                ans += (num * tot - val) + sum - val - tot * (nm - num);
            } --x; ++y;
        }
        memset(t1.t, 0, sizeof(int) * (2*n + 1));
        memset(t2.t, 0, sizeof(int) * (2*n + 1));
    } return cout<<ans, 0;
}

D [USACO23JAN] Tractor Paths P

神一样的倍增,屎一样的题面,对着题目瞪了有半个多小时……

不会倍增,看了题解,太™强拉!!!

首先,这些区间的左端点和右端点单调递增,所以如果想知道一个区间最远能到达哪里,就贪心地往最右边走就好了。所以可以使用倍增优化此过程。这是第一个答案求法。

对于第二问,维护特殊拖拉机的前缀和的倍增数组,沿着你倍增过来的路线加贡献即可。

#include<bits/stdc++.h>
using namespace std;
#define p pair<int, int>
constexpr int N = 2e5 + 5;
int n, m, L[N], R[N], lt[20][N], rt[20][N], sl[20][N], sr[20][N], sum[N];
string s1, s2;
inline int Dis(int u, int v){
	if(u == v) return 0;
	int dis = 0;
	for(int i=__lg(n); i>=0; --i) if(lt[i][u] != -1 && lt[i][u] < v)
		dis |= (1<<i), u = lt[i][u];
	return dis + 1;
}
int main(){
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin>>n>>m>>s1>>s2;
	for(int i=1, cnt=0, now=1; i<=n<<1; ++i){
		if(s1[i-1] == 'L') ++cnt;
		else R[now++] = cnt; 
	}
	for(int i=n<<1, cnt=n+1, now=n; i>=1; --i){
		if(s1[i-1] == 'R') --cnt;
		else L[now--] = cnt;
	}
	for(int i=1; i<=n; ++i) sum[i] = sum[i-1] + s2[i-1] - '0';
	memset(lt, 0xff, sizeof(lt)); memset(rt, 0xff, sizeof(rt));
	for(int i=1; i<=n; ++i) lt[0][i] = R[i], sl[0][i] = sum[R[i]];
	for(int i=1; i<=n; ++i) rt[0][i] = L[i], sr[0][i] = sum[L[i]-1];
	for(int i=1; i<=__lg(n); ++i) for(int j=1; j<n; ++j) if(lt[i-1][j] > 0){
		lt[i][j] = lt[i-1][lt[i-1][j]];
		if(lt[i][j] > 0) sl[i][j] = sl[i-1][j] + sl[i-1][lt[i-1][j]];
	}
	for(int i=1; i<=__lg(n); ++i) for(int j=n; j>1; --j) if(rt[i-1][j] > 0){
		rt[i][j] = rt[i-1][rt[i-1][j]];
		if(rt[i][j] > 0) sr[i][j] = sr[i-1][j] + sr[i-1][rt[i-1][j]];
	}
	while(m--){
		int u, v, dis; cin>>u>>v;
		cout<<(dis = Dis(u, v))<<' '; --dis;
		int ans = s2[u-1] - '0' + s2[v-1] - '0';
		if(u == v){ cout<<ans; continue; }
		for(int i=__lg(n); i>=0; --i) if(dis & (1<<i))
			ans += sl[i][u], u = lt[i][u];
		for(int i=__lg(n); i>=0; --i) if(dis & (1<<i))
			ans -= sr[i][v], v = rt[i][v];
		cout<<ans<<'\n';
	} return 0;
}

E [USACO23JAN] Mana Collection P

很巧妙的一道题

首先可以发现,当走过的点集确定之后,答案的大小仅与每个节点最后走到的时间有关,即为(\(d_i\) 为最后遍历的时间):

\[ans=\sum_{i=1}^{k}val_i*d_i \]

而贪心的说,对于每个节点的最后遍历时间越大,答案也就越大。一个节点的最短最后遍历时间即为 \(d_i=s-\sum_\limits{j=i}^{k-1}dis_{j,j+1}\),其中 \(dis\) 为两点间最短路径。那么带入刚才的狮子可得:

\[ans = s{\large \sum_{i = 1}^{k}} val[i]-{\large \sum_{i = 1}^{k-1}}dis(c_i, c_{i+1})\sum_{j = 1}^{i}val[j] \]

右边那一坨拿状压爆搞即可。

维护凸包或者使用李超线段树即可。复杂度 \(\mathcal{O}(N^3+N^22^N+q\log n)\)。说句闲话:我想用离线查询 + 单队维护凸包搞,于是调了一个下午和晚上(还没调出来)。有李超这么nb玩意,凸包

标签:训练,int,提高,ans,mid,long,组杂题,sum,id
From: https://www.cnblogs.com/xiaolemc/p/18461142

相关文章

  • 代码随想录训练营第五天|Leetcode.349,Leetcode.454,Leetcode19,Leetcode18
    一、哈希表和set和map和数组的关系 用哈希表,判断是否出现过。数值很大或者数值很分散时,不用数组,占用空间大,用set。set,multiset数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判......
  • 脉冲目标检测网络模型SpikeYOLO——基于整值训练和脉冲驱动推理的高性能节能目标检测
    最近看到目标检测领域又出新作,ECCV2024满分Oral论文——《Integer-ValuedTrainingandSpike-DrivenInferenceSpikingNeuralNetworkforHigh-performanceandEnergy-efficientObjectDetection》论文地址在这里,如下所示:感兴趣的话可以移步阅读原文,这里趁着中午午休......
  • [题目记录]一本通高手训练-数列
    题意定义n-数列为满足以下条件的数列\({a_i}\):数列长度不小于\(3\),且每个元素为\(1\)到\(n\)的整数.对于任意\(k\ge3\),有$(a_k-a_{k-2})(a_{k-1}-a_{k-2})<0$.给出\(n\),求n-数列的个数,对\(10^9+7\)取模.\(n\le10^{500000}\)时空限制......
  • K210 模型拍摄训练集,SD卡保存与查看
    在模型训练中,一般采用设备拍照训练的模型更加精确,理由如下:训练模型的图片为什么拍照比网图更好,可以从多个角度来分析:图像质量的重要性:高质量的图像在模型训练中至关重要。数据集中的图像质量不仅是一个额外的优势,而是训练过程中的必要条件。图像质量直接影响模型的理解和输......
  • DIKI:清华提出基于残差的可控持续学习方案,完美保持预训练知识 | ECCV'24
    本研究解决了领域-类别增量学习问题,这是一个现实但富有挑战性的持续学习场景,其中领域分布和目标类别在不同任务中变化。为应对这些多样化的任务,引入了预训练的视觉-语言模型(VLMs),因为它们具有很强的泛化能力。然而,这也引发了一个新问题:在适应新任务时,预训练VLMs中编码的知识可能会......
  • 代码随想录训练营第60天|冗余连接
    108.冗余连接#include<iostream>#include<vector>usingnamespacestd;intn;//节点数量vector<int>father(1001,0);//按照节点大小范围定义数组//并查集初始化voidinit(){for(inti=0;i<=n;++i){father[i]=i;}}//并查集......
  • Python知识点:基于Python技术,如何使用TensorFlow进行自动驾驶模型训练
    开篇,先说一个好消息,截止到2025年1月1日前,翻到文末找到我,赠送定制版的开题报告和任务书,先到先得!过期不候!使用TensorFlow进行自动驾驶模型训练的Python技术详解自动驾驶技术是人工智能领域的一个重要应用,它涉及到多个复杂的机器学习任务,如图像识别、决策制定和运动控制。Te......
  • 代码随想录算法训练营day12|144.二叉树的前序遍历 94.二叉树的中序遍历 145.二叉
    学习资料:https://programmercarl.com/二叉树理论基础.html二叉树:满二叉树、完全二叉树、二叉搜索数、平衡二叉搜索树;链式存储、顺序存储;前序/中序/后序遍历递归法、迭代法,层序深度优先搜索dfs,广度优先搜索学习记录:144.二叉树的前序遍历(也要注重二叉数的输入方式;递归法比迭......
  • tensorflow案例1--天气识别,包含(Tensorflow的检查是否GPU、图像数据加载与划分、拿取
    ......
  • 怎么样提高verilog代码编写水平?
    Q:怎么样提高verilog代码编写水平?Cpu从事DFT工作。目前仅限于写一些简单模块。自学的话如何提高verilog编写水平?A:以下是一些提高Verilog代码编写水平的自学方法:1.深入学习基础知识:重新巩固数字电路的基本概念,如逻辑门、组合逻辑、时序逻辑、状态机等,这是编写高质量Veri......