省选模拟 19/09/13

本文探讨了组合数问题的高效解决策略,包括使用堆优化选取最大值,以及通过log运算简化复杂度。同时,深入解析了字符串处理的高级技巧,如Trie树和链表优化的莫队算法,用于快速插入和删除操作。

组合数问题
很明显最下面的中间最大
把它放进堆,然后每次取最大,将它左右上分别放进堆中就可以了
现在的难题转换为求大小
一个套路就取 l o g log log 乘法转加法

#include<bits/stdc++.h>
#define N 1000050
using namespace std;
typedef long long ll;
const int Mod = 1000000007;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
int n, k;
ll fac[N], ifac[N], a[N];
bool cmp(ll a, ll b){ return a > b;}
ll mul(ll a, ll b){ return (a * b) % Mod;}
ll add(ll a, ll b){ return (a + b) % Mod;}
ll power(ll a, ll b){ ll ans = 1;
	for(;b;b>>=1){ if(b&1) ans = mul(ans, a); a = mul(a, a);}
	return ans;
}
ll C(int n, int m){ return mul(fac[n], mul(ifac[n-m], ifac[m]));}
typedef long double ld;
ld sum[N];
map<pair<int,int>, int> vis;
#define mp make_pair
struct Node{ 
	int x, y;
	friend bool operator < (const Node &a, const Node &b){
		return sum[a.x] + sum[b.y] + sum[b.x - b.y] < sum[b.x] + sum[a.y] + sum[a.x - a.y];
	}
} b[N];
priority_queue<Node> q;
int main(){
	n = read(), k = read();
	fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
	for(int i = 2; i <= n; i++) fac[i] = mul(fac[i-1], i);
	ifac[n] = power(fac[n], Mod-2);
	for(int i = n-1; i >= 2; i--) ifac[i] = mul(ifac[i+1], i+1);
	sum[0] = sum[1] = log(1);
	for(int i = 2; i <= n; i++) sum[i] = sum[i-1] + log(i);
	ll ans = 0; int cnt = 0;
	q.push((Node){n, n/2});
	while(cnt < k){
		Node now = q.top(); q.pop();
		if(vis[mp(now.x, now.y)]) continue;
		vis[mp(now.x, now.y)] = 1; 
		ans = add(ans, C(now.x, now.y));
		++cnt; 
		q.push((Node){now.x - 1, now.y});
		q.push((Node){now.x, now.y - 1});
		q.push((Node){now.x, now.y + 1});
	} cout << ans; return 0;
}

字符串
首先有个暴力的莫队做法,就是先建好 t r i e trie trie,然后每次插入删除字符串可以看做 t r i e trie trie树上的链加
复杂度 O ( n n ∗ l e n ) O(n\sqrt n*len) O(nn len)
注意到 ∑ s i ≤ 3 e 5 \sum s_i\le3e5 si3e5,于是可以把所有字符串拼在一起然后莫队,中途需要用 s e t set set维护一下
插入一个点,就是查前驱后继,减去大区间的贡献,加上两边区间的贡献
复杂度 O ( n n ∗ l o g n ) O(n\sqrt n*logn) O(nn logn) ,然后凉了
考虑用链表代替 s e t set set,应为链表不支持插入插入,支持删除,撤销,所以我们想到了一种回滚莫队的东西
实现如下----左端点相同按右端点从大到小排序,否则按左端点的块排序
然后按照块一块一块地处理
关于右端点的移动,每次从 n 往前挪,链表删除就是了
关于左端点的移动,每次撤回到块头,然后删除到 q l ql ql
右端点每块要移 n 次,是 n ∗ n n * \sqrt n nn 的,左端点每个询问最多移 n \sqrt n n 次,也是 n ∗ n n * \sqrt n nn

#include<bits/stdc++.h>
#define N 300050
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
typedef long long ll;
int ch[N][26], tot;
int dep[N], pos[N], val[N], node;
int n, m; ll F[N];
ll A, B, C;
int v[N], mxlen, len[N];
int g[N];
ll sum, ans[N]; int pre[N], suf[N];
int bel[N], seq[N], top, cnt[N];
struct Node{ int l, r, id;} q[N];
bool cmp(Node a, Node b){ 
	if(bel[a.l] == bel[b.l]) return a.r > b.r;
	return bel[a.l] < bel[b.l];
}
void ins(string s, int v){
	int len = s.length(), now = 0;
	for(int i = 0; i < len; i++){
		int c = s[i] - 'a'; 
		if(!ch[now][c]){ ch[now][c] = ++tot;}
		dep[ch[now][c]] = dep[now] + 1;
		now = ch[now][c]; pos[++node] = now; val[node] = v;
	}
}
bool ck(ll f, ll len){ if(f == 0) return 0; return B * f + len * A >= C;}
ll calc(int l, int r){ ll len = r-l+1; return len * (len+1) / 2;}
ll gcd(ll a, ll b){ return !b ? a : gcd(b, a % b);}
void add(int i){
	int x = pos[i];
	if(!ck(F[x], dep[x]) && ck(F[x] + val[i], dep[x]) && !cnt[g[dep[x]]]++){
		int k = g[dep[x]], l = pre[k], r = suf[k];
		suf[l] = pre[r] = k; sum -= calc(l+1, r-1); sum += calc(l+1, k-1); sum += calc(k+1, r-1);
	} F[x] += val[i];
}
void del(int i){
	int x = pos[i];
	if(ck(F[x], dep[x]) && !ck(F[x] - val[i], dep[x]) && !(--cnt[g[dep[x]]])){
		int k = g[dep[x]], l = pre[k], r = suf[k];
		suf[l] = r; pre[r] = l; sum -= calc(l+1, k-1); sum -= calc(k+1, r-1); sum += calc(l+1, r-1);
	} F[x] -= val[i];
}
int main(){
	n = read(), A = read(), B = read(), C = read();
	for(int i = 1; i <= n; i++) v[i] = read();
	for(int i = 1; i <= n; i++){ 
		string s; cin >> s; len[i] = s.length();
		mxlen = max(mxlen, len[i]);
		ins(s, v[i]); len[i] += len[i-1];
	}
	for(int i = 1; i <= mxlen; i++) g[i] = read();
	int siz = sqrt(node);
	for(int i = 1; i <= node; i++) bel[i] = (i-1) / siz + 1;
	m = read();
	for(int i = 1; i <= m; i++){
		int l = read(), r = read();
		q[i].l = len[l-1] + 1, q[i].r = len[r], q[i].id = i;
	} sort(q+1, q+m+1, cmp);
	for(int i = 1; i <= node; i++){
		int x = pos[i]; 
		if(!ck(F[x], dep[x]) && ck(F[x] + val[i], dep[x]) && !cnt[g[dep[x]]]++){
			seq[++top] = g[dep[x]]; 
		} F[x] += val[i];
	} seq[++top] = 0; seq[++top] = mxlen+1; 
	sort(seq+1, seq+top+1);
	for(int i = 1; i <= top; i++) pre[seq[i]] = seq[i-1], suf[seq[i]] = seq[i+1];
	for(int i = 2; i <= top; i++) sum += calc(seq[i-1]+1, seq[i]-1);
	int now = 1;
	int l = 1, r = node; 
	for(int i = 1, j = siz; i <= node; i += siz, j += siz){
		while(now <= m && q[now].l >= i && q[now].l <= j){
			while(r > q[now].r) del(r--);
			while(l < q[now].l) del(l++);
			ans[q[now].id] = sum; 
			while(l > i) add(--l); ++now;
		} while(r < node) add(++r); while(l <= j) del(l++);
	}
	ll tmp = 1ll * mxlen * (mxlen+1) / 2;
	for(int i = 1; i <= m; i++){
		ll v = tmp - ans[i];
		ll g = gcd(tmp, v); 
		cout << v / g << "/" << tmp / g << '\n';
	} return 0;
}

原题过于困难咕了,有一道类似的 SP11414 COT3 - Combat on a tree
博弈论两个基本定理
S G ( x ) = m e x ( S ) SG(x)=mex(S) SG(x)=mex(S)
一个游戏的 S G SG SG函数等于各个游戏的 S G SG SG函数的异或和
如果 S G SG SG为0,那么先手必败
看看本题
S G ( x ) SG(x) SG(x) 表示 x 的子树的SG函数
g [ x ] g[x] g[x] 表示 x 的子树的所有后继游戏局面的集合
S G ( x ) = m e x ( g [ x ] ) SG(x) = mex(g[x]) SG(x)=mex(g[x])
s u m [ x ] = S G ( v 1 ) ⨂ S G ( v 2 ) . . . ⨂ S G ( v k ) sum[x]=SG(v_1)\bigotimes SG(v_2)...\bigotimes SG(v_k) sum[x]=SG(v1)SG(v2)...SG(vk)
如果当前可以选,那么在 g [ x ] g[x] g[x]中插入 s u m [ x ] sum[x] sum[x]
然后考虑在当前 v k v_k vk的子树中选一个点删除,那么这个 v k v_k vk的子树一定是 g [ v k ] g[v_k] g[vk]中的一个值
发现我们需要把 g [ v k ] [ j ] ⨂ s u m [ x ] ⨂ S G ( v k ) g[v_k][j] \bigotimes sum[x]\bigotimes SG(v_k) g[vk][j]sum[x]SG(vk)合并到 g [ x ] g[x] g[x]
发现需要合并,求 m e x mex mex,用 0 / 1 t r i e 0/1trie 0/1trie解决
关于怎么得到答案
考虑枚举 u,然后就是删掉 u 到根的链后剩下的联通块的 S G SG SG异或起来,如果为0说明后手必败便可以最为答案,剩下的联通块的 S G SG SG异或可以 d f s dfs dfs时顺便求出

#include<bits/stdc++.h>
#define N 200050
#define M 20000050
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
int cov[M], ls[M], rs[M], tg[M];
int n, col[N];
int first[N], nxt[N], to[N], tot;
int sg[N], rt[N], node;
void add(int x, int y){ nxt[++tot] = first[x], first[x] = tot, to[tot] = y;}
void insert(int &x, int v, int i){
	if(!x) x = ++node; 
	if(i == -1){ cov[x] = 1; return;}
	if(v & (1<<i)) insert(rs[x], v, i-1);
	else insert(ls[x], v, i-1);
}
void pushxor(int &x, int val, int i){
	if(i <= -1) return;
	if(val & (1<<i)) swap(ls[x], rs[x]);
	tg[x] ^= val;
}
void pushdown(int x, int i){ 
	if(!tg[x] || !x) return;
	pushxor(ls[x], tg[x], i-1);
	pushxor(rs[x], tg[x], i-1);
	tg[x] = 0;
}
int merge(int x, int y, int i){
	if(!x || !y) return x + y;
	if(i == -1){ cov[x] |= cov[y]; return x;}
	pushdown(x, i); pushdown(y, i);
	ls[x] = merge(ls[x], ls[y], i-1);
	rs[x] = merge(rs[x], rs[y], i-1);
	cov[x] = cov[ls[x]] && cov[rs[x]];
	return x;
}
int mex(int x, int i){
	if(!x || i == -1) return 0;
	pushdown(x, i);
	if(!cov[ls[x]]) return mex(ls[x], i-1);
	else return (1 << i) + mex(rs[x], i-1);
}
void dfs(int u, int fa){
	int SG = 0;
	for(int i = first[u]; i; i = nxt[i]) if(to[i] ^ fa) dfs(to[i], u), SG ^= sg[to[i]];
	if(!col[u]) insert(rt[u], SG, 17);
	for(int i = first[u]; i; i = nxt[i]){
		int t = to[i]; if(t == fa) continue;
		pushxor(rt[t], SG ^ sg[t], 17);
		rt[u] = merge(rt[u], rt[t], 17);
	} sg[u] = mex(rt[u], 17);
}
vector<int> v;
void dfs2(int u, int fa, int SG){
	for(int i = first[u]; i; i = nxt[i]) if(to[i] ^ fa) SG ^= sg[to[i]];
	if(SG == 0 && !col[u]) v.push_back(u);
	for(int i = first[u]; i; i = nxt[i]){
		if(to[i] ^ fa) dfs2(to[i], u, SG ^ sg[to[i]]);
	}
}
int main(){
	n = read();
	for(int i = 1; i <= n; i++) col[i] = read();
	for(int i = 1; i < n; i++){ int x = read(), y = read(); add(x, y); add(y, x);}
	dfs(1, 0); dfs2(1, 0, 0);
	if(v.size()){
		sort(v.begin(), v.end());
		for(int i = 0; i < v.size(); i++) cout << v[i] << " ";
	}
	else cout << "-1"; return 0;
}
【论文复现】一种基于价格弹性矩阵的居民峰谷分时电价激励策略【需求响应】(Matlab代码实现)内容概要:本文介绍了一种基于价格弹性矩阵的居民峰谷分时电价激励策略,旨在通过需求响应机制优化电力系统的负荷分布。该研究利用Matlab进行代码实现,构建了居民用电行为与电价变动之间的价格弹性模型,通过分析不同时间段电价调整对用户用电习惯的影响,设计合理的峰谷电价方案,引导用户错峰用电,从而实现电网负荷的削峰填谷,提升电力系统运行效率与稳定性。文中详细阐述了价格弹性矩阵的构建方法、优化目标函数的设计以及求解算法的实现过程,并通过仿真验证了所提策略的有效性。; 适合人群:具备一定电力系统基础知识和Matlab编程能力,从事需求响应、电价机制研究或智能电网优化等相关领域的科研人员及研究生。; 使用场景及目标:①研究居民用电行为对电价变化的响应特性;②设计并仿真基于价格弹性矩阵的峰谷分时电价激励策略;③实现需求响应下的电力负荷优化调度;④为电力公司制定科学合理的电价政策提供理论支持和技术工具。; 阅读建议:建议读者结合提供的Matlab代码进行实践操作,深入理解价格弹性建模与优化求解过程,同时可参考文中方法拓展至其他需求响应场景,如工业用户、商业楼宇等,进一步提升研究的广度与深度。
针对TC275微控制器平台,基于AUTOSAR标准的引导加载程序实现方案 本方案详细阐述了一种专为英飞凌TC275系列微控制器设计的引导加载系统。该系统严格遵循汽车开放系统架构(AUTOSAR)规范进行开发,旨在实现可靠的应用程序刷写与启动管理功能。 核心设计严格遵循AUTOSAR分层软件架构。基础软件模块(BSW)的配置与管理完全符合标准要求,确保了与不同AUTOSAR兼容工具链及软件组件的无缝集成。引导加载程序本身作为独立的软件实体,实现了与上层应用软件的完全解耦,其功能涵盖启动阶段的硬件初始化、完整性校验、程序跳转逻辑以及通过指定通信接口(如CAN或以太网)接收和验证新软件数据包。 在具体实现层面,工程代码重点处理了TC275芯片特有的多核架构与内存映射机制。代码包含了对所有必要外设驱动(如Flash存储器驱动、通信控制器驱动)的初始化与抽象层封装,并设计了严谨的故障安全机制与回滚策略,以确保在软件更新过程中出现意外中断时,系统能够恢复到已知的稳定状态。整个引导流程的设计充分考虑了时序确定性、资源占用优化以及功能安全相关需求,为汽车电子控制单元的固件维护与升级提供了符合行业标准的底层支持。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值