luogu省选计划day1

T1

过水已隐藏

T2

整体二分经典题,主席树上二分也可以
在二分时要算一个国家的每一个出现位置,看起来是不行的但是均摊一下没有问题
使用线段树树状数组辅助计算答案
最大的答案会爆long long,开 ull

#include <iostream>
#include <vector>
#define int unsigned long long
//请注意,这道题longlong都撑不住,要ull
using namespace std;
const int maxN=3*1e5+10;
int n,m,k,idx;
int c[maxN],ans[maxN];//区间修改单点求值BIT
struct node {
	int na;
	int v;
}q[maxN];
int lx[maxN],rx[maxN],vx[maxN],req[maxN];
vector<int> nation[maxN];

inline int lowbit(int x){
	return (-x)&x;
}

inline int query(int x){
	//cout << 1 << endl;
	int ans=0;
	while(x)
		ans+=c[x],x-=lowbit(x);
	return ans;
}

inline void add(int x,int wh){
	//cout << 1 << endl;
	//cout << wh << endl;
	while(wh<=(maxN))
		c[wh]+=x,wh+=lowbit(wh);//cout << wh << endl;
}

inline void change(int l,int r,int v){
	add(v,l),add(-v,r+1);
}

void bsearch(int l,int r,int ql,int qr){
	//cout <<l << " " << r << ' '<< ql <<" "<< qr << endl;
	if(ql>qr) return ;
	if(l==r){
	//	cout << ql << " " << qr  << endl;
		for(int i=ql;i<=qr;++i)
			ans[q[i].na]=l;
		return ;
	}
	
	int mid=(l+r)>>1;
	for(int i=l;i<=mid;++i){
		if(lx[i]<=rx[i])
			change(lx[i],rx[i],vx[i]);
		else{
			change(lx[i],m,vx[i]);
			change(1,rx[i],vx[i]);
		}
	}
	//cout << query(1) << endl;
	vector<node> v1,v2;
	for(int i=ql;i<=qr;++i){
		int p=0;
		//cout << q[i].na << endl;
		for(int x:nation[q[i].na])
			p+=query(x);//cout << x << endl;
		if(q[i].v<=p)
			v1.push_back(q[i]);
		else q[i].v-=p,v2.push_back(q[i]);
	//	cout << p << endl;
	}
	for(int i=l;i<=mid;++i)
		if(lx[i]<=rx[i])
			change(lx[i],rx[i],-vx[i]);
	else
		change(lx[i],m,-vx[i]),change(1,rx[i],-vx[i]);
	for(int i=0;i<v1.size();++i)
		q[ql+i]=v1[i];
	for(int i=0;i<v2.size();++i)
		q[ql+v1.size()+i]=v2[i];
	
	if(v1.size())bsearch(l,mid,ql,ql+v1.size()-1);
	if(v2.size())bsearch(mid+1,r,ql+v1.size(),qr);
}



signed main(){
	ios::sync_with_stdio(false);//cin加速 
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);//再度加速 
	//不能用printf,scanf
	//强制在线时请勿与'\n'连用 
	cin >>n >>m;
	for(int i=1;i<=m;++i){
		int x;
		cin >> x;
		nation[x].push_back(i);
	}
	for(int i=1;i<=n;++i){
		cin >>q[i].v;
		req[i]=q[i].v;
		q[i].na=i;
	}
	cin >>k;
	for(int i=1;i<=k;++i)
		cin >>lx[i] >> rx[i] >>vx[i];
	//cout << 1 << endl;
	bsearch(1,k,1,n);
	for(int i=1;i<=k;++i){
		if(lx[i]<=rx[i])
			change(lx[i],rx[i],vx[i]);
		else{
			change(lx[i],m,vx[i]);
			change(1,rx[i],vx[i]);
		}
	}
	for(int i=1;i<=n;++i){
		int p=0;
		//cout << q[i].na << endl;
		for(int x:nation[i])
			p+=query(x);
		if(p<req[i]) cout << "NIE" << endl;
		else cout << ans[i] << endl;
	}
	
	return 114514-114514;
}
/*
  hello sir ,
  make sure that you aren't writing in 空格式。 
  
 */

T3

过水已隐藏

T4

裸的cdq分治三维偏序,但我现在还不会怎么cdq套cdq……
再高维的话就是二维数点的BIT了
可以用bitset并起来乱搞,复杂度\(O(\frac{n^2}{w})\)
但不能直接开 \(n^2\)bitset,这样会炸掉
我们可以只开一点点,比如每次开 \(\frac{n}{3}\) 个,清空再算
xpp:四维往上就跑不过暴力了

T5

神奇妙妙题
我们用ST表先预处理一下区间流量最大的东西向道路和南北向道路
在分治之前,先处理出边界的最长距离为0,分治一开始的区间是整个矩阵
我们每一次先选出区间内流量最大的东西向或南北向道路,这代表如果其走入了这条路,不会拐弯,则这条路上的点的最大值就是其到边界两条路中大的那一条,边界的值都已经算出

然后这条线会把当前的区间分为两份,继续递归
主定理可得分治的复杂度为\(O(H+W)\)
总复杂度为\(O((H+W)Q)\)
代码细节较多

T6

直接二进制拆分,用ST表预处理

T7

这里我们可以暴力枚举子串的起点
利用字符串匹配的性质,其肯定有单调性,我们进行二分,不断的枚举下一个不同的位置
具体的实现等在研究研究再补

T8

水省

T9

我们可以建一颗后缀树和前缀树,分别跑匹配的前后缀
然后可以利用dfn序的性质,将一个点在前缀树上的dfn序作为横坐标,后缀树为纵坐标
则此题转化为了一个二维数点的问题,将两个子树dfn序的范围取个交,这点分类讨论就可以
然后再将询问离线下来,做扫描线即可
复杂度单log

T10

异或有自反性,这里可以维护一个节点到根的异或和f[i]
则节点 i 到 j 的异或路径和为f[i]^f[j]
将所有的f[i]一起建一颗 01trie,对于每个值贪心选取就行
复杂度线性带 30 的常数

#pragma GCC optimize(2)
#include <iostream>
#include <vector>
#define pii pair<int,int>
#include <bitset>

using namespace std;

const int maxN=1e5+100;
struct node {
	int son[2];	
}tree[maxN*30];
vector<pii> g[maxN];
int n,f[maxN],idx=1;
bitset<maxN> vis;

void dfs(int now ){
	for(pii x:g[now]){
		if(vis[x.first]) continue ;
		vis[x.first] =1;
		f[x.first]=f[now]^x.second;
		dfs(x.first);
	}
}

void insert(int x){
	vector<int> v;
	for(int i=1;i<=32;++i){
		v.push_back((x&1));
		x>>=1;
	}
	int now=1;
	while(!v.empty()){
		int p=v.back();
		v.pop_back();
		if(!tree[now].son[p])
			tree[now].son[p]=++idx;
		now=tree[now].son[p];
	}
}

int enlarge(int x){
	int now=1,ans=0;
	vector<int> v;
	for(int i=1;i<=32;++i){
		v.push_back((x&1));
		x>>=1;
	}
	for(int i=31;i>=0;--i){
		if(tree[now].son[!v[i]]){
			ans+=(1<<i);
			now=tree[now].son[!v[i]];
		}
		else {
			now=tree[now].son[v[i]];
		}
	}
	return ans;
}

signed main(){
	ios::sync_with_stdio(false);//cin加速 
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);//再度加速 
	//不能用printf,scanf
	//强制在线时请勿与'\n'连用 
	cin >> n;
	for(int i=1;i<n;++i){
		int u,v,w;
		cin >> u >> v >> w ;
		g[u].push_back({v,w});
		g[v].push_back({u,w});
	}
	vis[1]=1;
	dfs(1);
	for(int i=1;i<=n;++i)
		insert(f[i]);
	int ans=0;
	for(int i=1;i<=n;++i)
		ans=max(enlarge(f[i]),ans);
	cout << ans << endl;
	
	
	
	return 114514-114514;
}
/*
  hello sir ,
  make sure that you aren't writing in 空格式。 
  
 */

T11

可以将柿子转化为\(\sum_{i=1}^{18} [f(a_{i}\, xor\, a_j)=i]*i\)
进一步可以推出\(\sum_{i=1}^{18} [f(a_{i}\, xor\, a_j)\geq i]\)
这里没乘系数,每一个数值会在小于它时多算,所以没有错
这样就可以在 01trie 上小DP一波了,我们枚举 \(10^i\) ,如果基准数当前位置上是 1 就只能走 1,若是 0 ,就把 1 部分的串个数加上,走 0

T12

kmp配合DP
我们定义f[i][j]为 s 走到了 i, t 走到了 j 的次数
如果 \(s_i=t_i\) 则直接转移,如果到了末尾,就答案加一
如果没有匹配,则我们与处理出来一个类似 fail 压缩的写法,找到当前 t 最长的有匹配的 border 进行处理
如果是问号,就枚举起边成哪个字符,用上方方法进行转移

posted @ 2022-12-11 00:26  颈流推进  阅读(72)  评论(0)    收藏  举报