QBLT2023NOIP实战 比赛汇总<2>

Day 7

ly哥哥代打,tql

100+70+100+0=270

rk2/58

评价:SSS+

T1

思路

给定一个初始串 s,咒语是由 s 无限循环得到的,如 s 为 aab,那么咒语为 aabaabaab...

接着国王每次选择一个区间 \(L,R\),复制第 \(L\) 个字母到第 \(R\) 个字母构成的子串,并把它插入到第 \(C\) 个字母后,特殊的,当 \(C=0\) 时代表插到了开头。

请你求出操作后第 \(k\) 个字母是什么?

如果在复制后位置的前面,则此次操作不起作用

如果在里面,则变换到对应位置

如果在后面,减去复制的长度即可

代码

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+5;
int n,m,k;
char s[1000005];
struct node{
    int l,r,c;
}q[N];
signed main(){
    scanf("%d%d%d",&n,&m,&k);
    scanf("%s",s+1);
    for(int i=1;i<=m;++i){
        scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].c);
    }
    for(int i=m;i>=1;i--){
        int l=q[i].l,r=q[i].r,c=q[i].c;
        if(c>=k) continue;
        else if(c+r-l+1>=k&&c<k){
            k=(l+k-c-1);
        }
        else if(c+r-l+1<k){
            k-=(r-l+1);
        }
    }
    cout<<s[k%n];
}


T2

思路

小 z 正在研究一种新的字符串匹配。

定义两个字符串等价 \(s \sim t\) 当前仅当存在一种对字符的重排方式使得 \(s\) 变换后和 \(t\) 相等。

如字符串 \(s\)aabc,那么将 a 变为 c,b 变为 a,c 变为 b,则 \(s\) 变为 ccab

我们考虑记录 \(nxt\),显然只有当 \(nxt\) 相同时两个字符串才能相同。

代码

点击查看代码
#include <bits/stdc++.h>
#define F(i,l,r) for(int i=l;i<=r;++i)
#define F2(i,l,r) for(int i=l;i>=r;--i)
using namespace std;
#define ll long long
const int N=1e6+50;
char s[N];
int n,f[N],nxt[N],T[31];
ll ans;
void KMP(){
    int j=0;
    F(i,2,n){
        while(j&&f[j+1]!=min(f[i],j+1)) j=nxt[j];
        if(f[j+1]==min(f[i],j+1)) ++j;nxt[i]=j;
        ans+=j;
    }
}
int main(){
    scanf("%s",s+1);
    n=strlen(s+1);
    F(i,1,n){
        f[i]=i-T[s[i]-'a'];
        T[s[i]-'a']=i;
    }
    KMP();
    printf("%lld",ans);
    return 0;
}

T3

思路

给定一个长度为 n 的正整数序列,求有多少个区间满足其元素的乘积开 k 次根号为整数。

显然我们可以分解质因数,一个数的开 \(k\) 方是整数当且仅当这个数的所有质因子都能被k整除。

我们可以 hash 维护所有串,然后用 map 存一下左端点,在右端点处询问即可。

代码

点击查看代码
#include <bits/stdc++.h>

using namespace std;
typedef unsigned long long ull;

const int N = 1e5 + 10, W = 1e7 + 10, BASE = 131;
int n, k, A[N];

inline ull ksm(ull a, int b) {
	ull r = 1;
	while(b) {
		if(b & 1) r = r * a;
		b >>= 1, a = a * a;
	}
	return r;
}

vector<int> primes;
int d[W], pos[W];
inline void Sieve(const int N = (int)1e7) {
	for(int i = 2; i <= N; i ++) {
		if(!d[i]) pos[i] = primes.size(), d[i] = i, primes.emplace_back(i);
		for(auto p : primes) {
			int j = p * i; if(j > N) break;
			d[j] = p; if(i % p == 0) break;
		}
	}
}

ull Hash = 0; int c[W];
map<ull, int> Map;
inline void insert(int x) {
	while(x > 1) {
		c[d[x]] ++;
		Hash += 1ull * ksm(BASE, pos[d[x]]);
		if(c[d[x]] == k) Hash -= 1ull * k * ksm(BASE, pos[d[x]]), c[d[x]] = 0;
		x /= d[x];
	}
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0);
	Sieve();

	cin >> n >> k;
	for(int i = 1; i <= n; i ++)
		cin >> A[i];

	Map[Hash] ++; long long ans = 0;
	for(int i = 1; i <= n; i ++) {
		insert(A[i]);
		ans += Map[Hash] ++;
	}
	cout << ans << '\n';

	return 0;
}

T4

思路

游戏规则如下:

在游戏开始时,有 n 个 01 字符串。Alice 和 Bob 轮流从这些字符串中选择一个,直到所有的字符串都被选完。当 Alice 和 Bob 选择了一些字符串后,她可以计算这些字符串组成的集合 S 的价值。价值由集合中字符串的权值和集合中所有字符串对的最长公共前缀(Lcp)之和决定,但不包括字符串本身与自己的对。为了取得胜利,双方都希望自己的价值减去对方的价值最大。

作为这个国度的扇贝,你的任务是计算出在双方都采取最优策略的情况下,这个价值差是多少。

考虑设题目在求两者的权值之差,使得串的权值为 \(v_i+\frac1 2 \sum\limits_{j}\)

然后对此排序,从大到小选择。

加入人同时,两个\(\frac 1 2\)\(1\)

加入人不同时,两个\(\frac 1 2\)\(0\)

代码

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
int n; 
struct node {
	string s; long long w;
	inline void input() {cin >> s >> w; }
};
vector<node> A;

const int M = 1e7 + 5;
int pos[N], to[M][2], fig[2 * M], fa[2 * M], cnt = 0;
inline int insert(const string &s) {
	int u = 0;
	for(int i = 0; i < s.size(); i ++) {
		if(!to[u][s[i] - '0']) to[u][s[i] - '0'] = ++cnt, fa[to[u][s[i] - '0']] = u;
		u = to[u][s[i] - '0'], fig[u] ++;
	}
	return u;
}
inline int get(int u) {
	int ans = 0;
	while(u) {
		ans += fig[u];
		u = fa[u];
	}
	return ans;
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin >> n; A.resize(n);
	for(int i = 0; i < n; i ++)
		A[i].input(), A[i].w *= 2;

	for(int i = 0; i < n; i ++) 
		pos[i] = insert(A[i].s);
	for(int i = 0; i < n; i ++)
		A[i].w += get(pos[i]);
	sort(A.begin(), A.end(), [&](const node a, const node b) {return a.w > b.w; });
	long long ans = 0;
	for(int i = 0; i < n; i ++) {
		if(i & 1) ans -= A[i].w;
		else ans += A[i].w;
	}
	assert(ans % 2 == 0);
	cout << (ans >> 1) << '\n';

	return 0;
}

Day 8

100+100+100+60=360

rk3/58

评价:SSS+

T1

思路

给定一棵带点权的树,第 i 个点的点权为 \(v_i\)

一条链 \(a_1,a_2,\cdots,a_k\) 的权值定义为 \(a_1\oplus a_2 + a_2 \oplus a_3 + \cdots + a_{k-1} \oplus a_k\)

求树上最大权值的一条链的权值。

\(u\)\(v\) 的边权看成 \(u \oplus v\),跑直径即可。

代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=5e5+10;
struct edge{
	int nt,to;
}e[N<<1];
int p[N],cnt;
inline void add(int x,int y){
	e[++cnt]={p[x],y};p[x]=cnt;
}
int a[N],n,ans;
int dp[N];
void dfs(int x){
	int mx=0,mxs=0;
	for(int i=p[x];i;i=e[i].nt){
		int v=e[i].to;
		dfs(v);
		int now=dp[v]+(a[x]^a[v]);
		dp[x]=max(dp[x],now);		
		if(now>mx){mxs=mx;mx=now;}
		else if(now>mxs)mxs=now;
	}
	ans=max(ans,mx+mxs);
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=2;i<=n;i++){
		int x;
		cin>>x;
		add(x,i);
	}
	dfs(1);
	cout<<ans;
	return 0;
}

T2

思路

在一个名为“提瓦特”的大陆里,有一个神秘的迷宫。这个迷宫由许多房间组成,每个房间都有一个独特的编号。房间之间由单向通道连接,形成一个有向无环图(可能有重边)。王国的勇士们需要在迷宫中找到了宝藏,但是宝藏的需要特殊的咒语,这个咒语和满足特定条件的房间对有关。

这个特定条件就是:对于房间 \((x,y)\)\(x\),且从房间 x 到房间 y 的不同路径个数为奇数。作为王国的智者,你的任务是计算出有多少这样的房间对。

我们设 \(f_{u,v}\)代表从 \(u\)\(v\) 的路径条数是鸡数还是偶数,鸡数为 1 ,偶数为 0。

bitset 优化即可。

代码

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e4 + 5;
queue<int> q;
vector<int> G[N];
bitset<N> dp[N];
int in[N], n, m, res;
signed main() {
	cin >> n >> m;
	for (int i = 1; i <= m; i++) {
    	int x, y;
		cin >> x >> y;
		G[x].push_back(y); ++in[y];
	}
	for (int i = 1; i <= n; i++) {
		dp[i][i] = 1;
		if (!in[i]) q.push(i);
	}
	int cnt = 0;
	while (q.size()) {
		cnt++;
		int x = q.front(); q.pop();
		res += dp[x].count() - 1;
		for (auto e : G[x]) {
			dp[e] ^= dp[x];
			if (!--in[e]) q.push(e);
		}
	}
	cout << res << endl;
}

T3

思路

在一个遥远的星球上,有一种神奇的生物叫做“星树”,星树的生长过程非常特殊。星树的每个叶子节点都可以赋予一个权。在这个星球上,有 k 个叶子节点,星树的智者们有 k 个神秘的权值石,对应的权值为 1 到 k,智者中的长老已经分配一些叶子的权值,他们需要把剩下权值石分配给其他的个叶子节点。

星树的每个非叶子节点的权值取决于其子节点的权值,具体来说,非叶子节点的权值等于其子节点权值的中位数(具体地,如果有 k 个孩子,那么权值为排名 \(\lceil \frac k2 \rceil\) 的孩子权值)。星树的智者们希望通过合理分配权值石,使得星树的根节点的权值达到最大。

你作为星树的智者,需要找到一种分配方案,使得根节点的权值最大。

二分答案。

\(f_x\) 为在 \(x\) 最小分配多少个权值,使得 \(x\) 的权值 \(\ge mid\)

那么就是选择 \(\frac k 2\) 个儿子,那么 \(f_x=\sum f_y\)所以将儿子按照 \(f_x\) 排序,从小到大选择,最终判断 \(f_x\) 是否小于等于可分配的叶子权值个数。

代码

点击查看代码
#include <bits/stdc++.h>
using namespace std;

const int N = 5e5 + 5;
const int inf = N; 
vector<int> G[N];
int dp[N], v[N];
int n, k, mid, tmp;

void dfs(int x) {
	if (v[x]) tmp -= v[x] >= mid;
	if (G[x].empty()) return dp[x] = v[x] ? (v[x] >= mid ? 0 : inf) : 1, void();
	for (auto e : G[x]) dfs(e);
	sort(G[x].begin(), G[x].end(), [&](int a, int b) { return dp[a] < dp[b]; });
	int k = G[x].size() / 2 + 1;
	dp[x] = 0;
	for (int i = 0; i < k; i++) dp[x] += dp[G[x][i]];
}

int main() {
	cin >> n >> k;
	for (int i = 2; i <= n; i++) {
		int x;
		cin >> x;
		G[x].push_back(i);
	}
	for (int i = n - k + 1; i <= n; i++) cin >> v[i];
	int l = 1, r = k;
	while (l <= r) {
		mid = (l + r) >> 1, tmp = k - mid + 1, dfs(1);
		if (dp[1] > tmp) r = mid - 1;
		else l = mid + 1;
	}
	cout << r << endl;
}

T4

思路

考虑从上到下分配集合,或者按 DFS 序分配,保证祖先在自己之前分配集合即可。
那么有\(f[x][y]=f[x-1][y] \times (y-dep)+f[x-1][y-1]\) ,当 $ y \le dep $ 时。
这代表放到之前的集合内(不能和祖先放在一起,其他的集合任意放),或者自己新建
一个集合。

代码

点击查看代码
#include<bits/stdc++.h>
#define mod 1000000007
#define N 100005
#define ll long long
using namespace std;
int head[N],Next[N],to[N],nedge,cnt,m,n,dp[N][105];
void add(int a,int b){
    Next[++nedge]=head[a],head[a]=nedge,to[nedge]=b;
    }
void dfs(int u,int dep){
	cnt++;
	for(int i=dep;i<=m;i++)
    dp[cnt][i]=(1ll*dp[cnt-1][i]*(i-dep+1)+dp[cnt-1][i-1])%mod;
	for(int i=head[u];i;i=Next[i])
    dfs(to[i],dep+1);
}

int main(){
	cin>>n>>m;
	dp[0][0]=1;
	for(int i=2;i<=n;i++){
		int u;
		cin>>u;
		add(u,i);
	}
	dfs(1,1);
	cout<<dp[cnt][m];
	return 0;
}

Day9

A

原题面(在洛谷上自己写的)

T1传送门

思路:

直接暴力 next_permutation 或者暴力搜索即可, 需要模拟一个简单的表达式解析。

时间复杂度 \(O(n!)\)

代码

神奇的code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5;
int a[N],woshuosousuonixinma[N],c[N],n,m,b[N],Y,cnt;
string s;
char ans;
stack<char>S;
stack<int>S1; 
bool woshuosousuonixinmaer(){
	cnt=0;
	while(!S1.empty())S1.pop();
	for(int i=0;i<s.length();i++){
		if(s[i]>='a'&&s[i]<='z'){
			S1.push(b[++cnt]);
			continue;
		}
		if(s[i]==')'){
			int cnt=0;
			while(S.top()!='('){
				ans=S.top();cnt++;
				S.pop();
			}
			int t2=S1.top();S1.pop();
			int t1=S1.top();S1.pop(); 
			assert(cnt==1);
			S.pop();
			if(ans=='+')S1.push(t1+t2);
			if(ans=='-')S1.push(t1-t2);
			if(ans=='*')S1.push(t1*t2);
			continue;
		}
		S.push(s[i]);
	}
	if(S1.top()==m)return 1;
	else return 0;
}
void defesi(int x){
	if(x==n+1){
		for(int i=1;i<=n;i++){
			b[i]=a[c[i]];
		}
		if(woshuosousuonixinmaer()){
			cout<<"YES"<<"\n";
			Y=1;
		}
		return;
	}
	for(int i=1;i<=n;i++){
		if(!woshuosousuonixinma[i]){
			woshuosousuonixinma[i]=1;
			c[x]=i;
			defesi(x+1);
			if(Y==1){
				return;
			}
			woshuosousuonixinma[i]=0;
		} 
	}
}
void solve(){
	for(int i=1;i<=n;i++)
	woshuosousuonixinma[i]=0;
	Y=0;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	cin>>m;
	cin>>s;
	defesi(1);
	if(Y==0){
	cout<<"NO"<<"\n";
	}
	return;
}
signed main(){
	int T;
	cin>>T;
	while(T--){
		solve(); 
	}
	return 0;
}

B

原题面

T2传送门

思路

考虑直接暴力搜索每个位置放了或者没有放, 可能的优化:

  • 如果某一行/某一列超过限制了或者全选了也不够,直接剪枝
  • 从最后一行/最后一列开始搜索,因为权值大能够对后面有比较大的影响。

时间复杂度 \(O(能过)\)

代码

点击查看代码
#include <bits/stdc++.h>

using namespace std;

const int N = 17;
int n, m, r[N], c[N];

bool flag = false;
int A[N][N];

void dfs(int x, int y) {
	if(y == 0) {
		if(r[x]) return;
		else y = m, x --;
	}
    if((y + 1) * y / 2 < r[x]) return;
    if((x + 1) * x / 2 < c[y]) return;
	if(x == 0) {
		for(int i = 1; i <= m; i ++) if(c[i]) return;
		flag = true; return; 
	}
	if(r[x] >= y && c[y] >= x) {
		if(x != 1 || !(c[y] - x)) {
			r[x] -= y, c[y] -= x;
			A[x][y] = 1; dfs(x, y - 1); if(flag) return;
			r[x] += y, c[y] += x;
		}
	}
	if(x * (x - 1) / 2 >= c[y]) A[x][y] = 0, dfs(x, y - 1);
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0);

	int T; cin >> T;
	while(T --) { flag = false;
		cin >> n >> m;
		for(int i = 1; i <= n; i ++) cin >> r[i];
		for(int i = 1; i <= m; i ++) cin >> c[i];
		dfs(n, m); assert(flag);
		for(int i = 1; i <= n; i ++)
			for(int j = 1; j <= m; j ++)
				cout << A[i][j] << " \n"[j == m];
	}

	return 0;
}

C

我们本质上就是需要构造一个有向图,我们可以把重边的重数看成边权,同时上面的字母是不重要的。考虑怎么通过搜索构造这个有向图。

我们需要确定的东西: 终点集合,边权。

整体搜索的想法就是一个一个点的加上,搜索连边情况剪掉不用的状态

  • 如果某个时刻能够接受的状态已经超过了,剪枝
  • 限制每条边的边权上界
  • 在估计某个时刻能够接受的状态的时候加上不能走到终点的点的贡献,因为这些点一定在之后的某个时刻能够走到终点。
  • ...

最后通过限制我们发现所有点都在 \(n\le 4\) 有解,在限制了边权上界为 \(4\) 能够很快的求解出答案。

代码

还打表
#include<bits/stdc++.h>
using namespace std;
int main(){
	int a;
	cin>>a;
	if(a==122){
		cout<<"4 4"<<endl;
		cout<<"1 0 0 0"<<endl;
		cout<<"1 4 0 0 "<<endl;
		cout<<"1 4 4 4 "<<endl;
		cout<<"1 2 2 2 "<<endl;
		cout<<"1 3 4 0"<<endl;
	}
	if(a==124){
		cout<<"4 5"<<endl;
		cout<<"1 0 0 0"<<endl;
		cout<<"1 4 0 0 0 "<<endl;
		cout<<"3 0 0 0 0 "<<endl;
		cout<<"1 2 2 4 4 "<<endl;
		cout<<"1 2 3 3 0 "<<endl;
	}
	if(a==126){
		cout<<"4 5"<<endl;
		cout<<"1 0 0 0"<<endl;
		cout<<"1 4 0 0 0 "<<endl;
		cout<<"3 0 0 0 0 "<<endl;
		cout<<"1 2 2 3 4"<<endl;
		cout<<"1 2 3 3 0"<<endl;
	}
	if(a==128){
		cout<<"4 6"<<endl;
		cout<<"1 0 0 0"<<endl;
		cout<<"1 4 0 0 0 0 "<<endl;
		cout<<"3 0 0 0 0 0 "<<endl;
		cout<<"2 4 0 0 0 0 "<<endl;
		cout<<"1 2 2 3 4 4 "<<endl;
	}
	if(a==130){
		cout<<"4 5"<<endl;
		cout<<"1 0 0 0"<<endl;
		cout<<"1 4 0 0 0 "<<endl;
		cout<<"3 0 0 0 0 "<<endl;
		cout<<"2 3 4 0 0 "<<endl;
		cout<<"1 2 3 4 4 "<<endl;
	}
	if(a==132){
		cout<<"4 4"<<endl;
		cout<<"1 0 0 0"<<endl;
		cout<<"1 4 0 0 "<<endl;
		cout<<"3 0 0 0"<<endl;
		cout<<"2 3 3 4 "<<endl;
		cout<<"1 3 4 4 "<<endl;
	}
	if(a==134){
		cout<<"4 6"<<endl;
		cout<<"1 0 0 0"<<endl;
		cout<<"1 4 0 0 0 0"<<endl;
		cout<<"3 0 0 0 0 0"<<endl;
		cout<<"2 2 2 3 3 4"<<endl;
		cout<<"1 3 4 4 0 0"<<endl;
	}
	if(a==136){
		cout<<"4 8"<<endl;
		cout<<"1 0 0 0"<<endl;
		cout<<"1 4 0 0 0 0 0 0 "<<endl;
		cout<<"3 4 0 0 0 0 0 0 "<<endl;
		cout<<"2 2 2 3 3 3 4 4 "<<endl;
		cout<<"1 2 4 4 0 0 0 0 "<<endl;
	}
	if(a==138){
		cout<<"4 9"<<endl;
		cout<<"1 0 0 0"<<endl;
		cout<<"1 4 0 0 0 0 0 0 0"<<endl;
		cout<<"3 0 0 0 0 0 0 0 0"<<endl;
		cout<<"1 2 2 2 2 3 3 3 3"<<endl;
		cout<<"1 2 4 4 0 0 0 0 0"<<endl;
	}
	if(a==140){
		cout<<"4 6"<<endl;
		cout<<"1 0 0 0"<<endl;
		cout<<"1 4 0 0 0 0"<<endl;
		cout<<"3 3 3 0 0 0"<<endl;
		cout<<"2 2 2 3 3 4"<<endl;
		cout<<"1 3 4 4 0 0"<<endl;
	}
}

时间复杂度 \(O(能过)\)

D 打表,必须打表,打表100捏

考虑搜索平方之前的数, 我们可以发现如果确定了一个后缀那么平方之后的一个后缀也确定了,同时如果确定了一个前缀,平方之后的前缀也能规范到一个区间里面。

于是我们就可以按照后面一个数前面一个数的顺序进行搜索,如果某个时刻确定的后缀没有办法匹配当前的前缀区间就剪枝。

这种做法可以在一个小时之内算出所有平方前 \(10^{17}\) 的答案,也就是数据范围里的 \(k\le 485\).

代码

神奇的code(打表)
#include <bits/stdc++.h>

using namespace std;
#define int long long
const string a[]={"0","1","4","9","121","484","676","10201","12321","14641","40804","44944","69696","94249","698896","1002001","1234321","4008004","5221225","6948496","100020001","102030201","104060401","121242121","123454321","125686521","400080004","404090404","522808225","617323716","942060249","10000200001","10221412201","12102420121","12345654321","40000800004","637832238736","1000002000001","1002003002001","1004006004001","1020304030201","1022325232201","1024348434201","1086078706801","1210024200121","1212225222121","1214428244121","1230127210321","1232346432321","1234567654321","1615108015161","4000008000004","4004009004004","4051154511504","5265533355625","9420645460249","100000020000001","100220141022001","102012040210201","102234363432201","121000242000121","121242363242121","123212464212321","123456787654321","123862676268321","144678292876441","165551171155561","400000080000004","900075181570009","4099923883299904","10000000200000001","10002000300020001","10004000600040001","10020210401202001","10022212521222001","10024214841242001","10201020402010201","10203040504030201","10205060806050201","10221432623412201","10223454745432201","12100002420000121","12102202520220121","12104402820440121","12120030703002121","12122232623222121","12124434743442121","12321024642012321","12323244744232321","12341234943214321","12343456865434321","12345678987654321","40000000800000004","40004000900040004","94206450305460249","1000000002000000001","1000220014100220001","1002003004003002001","1002223236323222001","1020100204020010201","1020322416142230201","1022123226223212201","1022345658565432201","1210000024200000121","1210242036302420121","1212203226223022121","1212445458545442121","1232100246420012321","1232344458544432321","1234323468643234321","4000000008000000004","4253436912196343524","6158453974793548516","100000000020000000001","100002000030000200001","100004000060000400001","100020201040102020001","100022201252102220001","100024201484102420001","100200120040021002001","100202122050221202001","100204124080421402001","100220341262143022001","100222343474343222001","102010002040200010201","102012022050220210201","102014042080240410201","102030405060504030201","102032425272524230201","102132537636735231201","102210100272001012201","102212122262221212201","102214144272441412201","102230523292325032201","102232545484545232201","102234567696765432201","104190107303701091401","121000000242000000121","121002200252002200121","121004400282004400121","121020021070120020121","121022221262122220121","121024421474124420121","121220122262221022121","121222324272423222121","121240161292161042121","121242363484363242121","121244565696565442121","123210002464200012321","123212222474222212321","123230205292502032321","123232425484524232321","123234645696546432321","123432124686421234321","123434346696643434321","184398883818388893481","400000000080000000004","400004000090000400004","522815090696090518225","906086675171576680609","942064503484305460249","6916103777337773016196","10000000000200000000001","10000220001410002200001","10002002100400120020001","10002222123632122220001","10020010200400201002001","10020230421612403202001","10022014302620341022001","10022234545854543222001","10201000020402000010201","10201222221612222210201","10203022140604122030201","10203244363836344230201","10221210222622201212201","10221432643834623412201","10223234344844343232201","10224609234443290642201","12100000002420000000121","12100242003630024200121","12102202302620320220121","12102444325852344420121","12122010222622201022121","12122252443834425222121","12124214524842541242121","12321000024642000012321","12321244225852244212321","12323222344844322232321","12343210246864201234321","12384043938083934048321","12599536942224963599521","16593841302620314839561","40000000000800000000004","1000000000002000000000001","1000002000003000002000001","1000004000006000004000001","1000020200104010020200001","1000022200125210022200001","1000024200148410024200001","1000200030004000300020001","1000202030205020302020001","1000204030408040304020001","1000220232126212320220001","1000222232347432322220001","1002001002004002001002001","1002003004005004003002001","1002005006008006005002001","1002021222306032221202001","1002023224327234223202001","1002201232026202321022001","1002203234227224323022001","1002221454348434541222001","1002223456569656543222001","1020100000204020000010201","1020102020205020202010201","1020104040208020404010201","1020120402306032040210201","1020122422327232242210201","1020300010207020100030201","1020302030406040302030201","1020304050607060504030201","1020320414309034140230201","1020322434528254342230201","1020324454749474544230201","1022121002226222001212201","1022123024227224203212201","1022141424528254241412201","1022143446549456443412201","1022321210249420121232201","1022323232448442323232201","1022325254649464525232201","1210000000024200000000121","1210002200025200022000121","1210004400028200044000121","1210020020107010200200121","1210022220126210222200121","1210024420147410244200121","1210220032026202300220121","1210222232227222322220121","1210242254148414522420121","1210244454369634544420121","1212201002226222001022121","1212203204227224023022121","1212221040509050401222121","1212223242528252423222121","1212225444549454445222121","1212421234248424321242121","1212423436449446343242121","1232100000246420000012321","1232102220247420222012321","1232120202329232020212321","1232122422348432242212321","1232124642369632464212321","1232322032448442302232321","1232324252649462524232321","1234321002468642001234321","1234323224469644223234321","1821056104269624016501281","4000000000008000000000004","4000004000009000004000004","4618627222542452227268164","6942236477330337746322496","9420645034800084305460249","40460195511188111559106404","100000000000020000000000001","100000220000141000022000001","100002002010040010200200001","100002222012363210222200001","100020001200040002100020001","100020221222161222122020001","100022003410262014300220001","100022223434585434322220001","100200100020040020001002001","100200320240161042023002001","100202104032060230401202001","100202324254383452423202001","100220121220262022121022001","100220341462383264143022001","100222125432484234521222001","102010000002040200000010201","102010222202161202222010201","102012022032060230220210201","102012244234383432442210201","102030201204060402102030201","102030423426181624324030201","102032223434282434322230201","102212100022262220001212201","102212322442383244223212201","102214124054282450421412201","102232321224484422123232201","121000000000242000000000121","121000242000363000242000121","121002202210262012202200121","121002444212585212444200121","121022001220262022100220121","121022243242383242342220121","121024203630484036302420121","121220100022262220001022121","121220342242383242243022121","121222304234282432403222121","121242121242484242121242121","123210000002464200000012321","123210244202585202442012321","123212222232484232222212321","123232201224484422102232321","123432100024686420001234321","400000000000080000000000004","923860899791363197998068329","4872133543202112023453312784","9658137819052882509187318569","10000000000000200000000000001","10000002000000300000020000001","10000004000000600000040000001","10000020200010401000202000001","10000022200012521000222000001","10000024200014841000242000001","10000200021000400012000200001","10000202021020502012020200001","10000204021040804012040200001","10000220221212621212202200001","10000222221234743212222200001","10002000102000400020100020001","10002002102200500220120020001","10002004102400800420140020001","10002020304030603040302020001","10002022304232723240322020001","10002200143002620034100220001","10002202143222722234120220001","10002220345234843254302220001","10002222345456965454322220001","10020010000200400200001002001","10020012002200500220021002001","10020014004200800240041002001","10020030220410601402203002001","10020032222412721422223002001","10020210221220602212201202001","10020212223240704232221202001","10020230441632823614403202001","10020232443654945634423202001","10022010100002720000101022001","10022012102202620220121022001","10022014104402720440141022001","10022030322230903222303022001","10022032324432823442323022001","10022034326634943662343022001","10022210341004940014301222001","10022212343224842234321222001","10022214345444944454341222001","10201000000020402000000010201","10201002020020502002020010201","10201004040020802004040010201","10201020402030603020402010201","10201022422032723022422010201","10201200001200700210000210201","10201202021220602212020210201","10201204041240704214040210201","10201220403410901430402210201","10201222423432823432422210201","10201224443454945434442210201","10203020102040604020102030201","10203022122240704222122030201","10203040506070807060504030201","10203042526272927262524030201","10203220123022922032102230201","10203222143242824234122230201","10203224163462926436142230201","10221210000222622200001212201","10221212022222722222021212201","10221230422432823422403212201","10221232444434943444423212201","10221412221442824412221412201","10221414243462926434241412201","10223232102244844220123232201","10223234124444944442143232201","12100000000002420000000000121","12100002200002520000220000121","12100004400002820000440000121","12100020020010701002002000121","12100022220012621002222000121","12100024420014741002442000121","12100220023002620032002200121","12100222223022722032222200121","12100242243214841234224200121","12100244443236963234444200121","12102200102202620220100220121","12102202302402720420320220121","12102220124030903042102220121","12102222324232823242322220121","12102224524434943442542220121","12102420145204840254102420121","12102422345424942454322420121","12122010000222622200001022121","12122012202222722220221022121","12122032240432823404223022121","12122034442434943424443022121","12122230223242824232203222121","12122232425262926252423222121","12124212102424842420121242121","12124214304624942640341242121","12321000000024642000000012321","12321002220024742002220012321","12321020202032923020202012321","12321022422034843022422012321","12321024642036963024642012321","12321222023224842232022212321","12321224243244944234242212321","12323220102244844220102232321","12323222322444944422322232321","12343210000246864200001234321","12343212222246964222221234321","16799008923862526832980099761","40000000000000800000000000004","40000004000000900000040000004","44431002775280908257720013444","98693567900935453900976539689","1000000000000002000000000000001","1000000220000014100000220000001","1000002002001004001002002000001","1000002222001236321002222000001","1000020000300004000030000200001","1000020220302216122030220200001","1000022002321026201232002200001","1000022222323458543232222200001","1000200010020004000200100020001","1000200230042016102400320020001","1000202012221206021222102020001","1000202232243438343422322020001","1000220012320026200232100220001","1000220232344238324432320220001","1000222014541248421454102220001","1002001000002004002000001002001","1002001220222016102220221002001","1002003004005006005004003002001","1002003224225238325224223002001","1002021020302206022030201202001","1002021240524418144250421202001","1002023024325228225234203202001","1002201210022026202200121022001","1002201430264038304620341022001","1002203214225228225224123022001","1002221232322248422232321222001","1020100000000204020000000010201","1020100222200216120022220010201","1020102022021206021202202010201","1020102244221438341224422010201","1020120200302206022030020210201","1020120422504418144052240210201","1020122222343228223432222210201","1020302010020406040200102030201","1020302232242418142422322030201","1020304032241608061422304030201","1020322212322428242232122230201","1022121000002226222000001212201","1022121222422238322242221212201","1022123024025228225204203212201","1022141220304428244030221412201","1022323210022448442200123232201","1210000000000024200000000000121","1210000242000036300002420000121","1210002202201026201022022000121","1210002444201258521024442000121","1210022000320026200230002200121","1210022242322238322232422200121","1210024202541048401452024200121","1210220010022026202200100220121","1210220252044038304402520220121","1210222212423228223242122220121","1210242012342048402432102420121","1212201000002226222000001022121","1212201242222238322222421022121","1212203204205228225024023022121","1212223020322428242230203222121","1212421210024248424200121242121","1232100000000246420000000012321","1232100244200258520024420012321","1232102222221248421222222012321","1232122200322248422230022212321","1232322010022448442200102232321","1234321000002468642000001234321","4000000000000008000000000000004","4844486878939076709398786844484","6574372239019762679109322734756","9403095533541415141453355903049","9659504223792743472973224059569","9848294822582726272852284928489","46501623417708833880771432610564","100000000000000020000000000000001","100000002000000030000000200000001","100000004000000060000000400000001","100000020200001040100002020000001","100000022200001252100002220000001","100000024200001484100002420000001","100000200020100040001020002000001","100000202020102050201020202000001","100000204020104080401020402000001","100000220220121262121022022000001","100000222220123474321022222000001","100002000012000040000210000200001","100002002012020050020210200200001","100002004012040080040210400200001","100002020212203060302212020200001"};

signed main(){
	int n,m;
	cin>>n;
	while(n--){
		cin>>m;
		cout<<a[m]<<"\n";
	}
	return 0;
}

Day10

A

注意到我们没有必要在绿灯的时候等, 所以直接跑 dijkstra, 然后每次计算一下下一次绿灯是什么时候即可。

时间复杂度 \(O(m \log n)\)

点击查看代码
#include <iostream>
#include <vector>
#include <queue>
#include <cmath>
#define int long long
using namespace std;

const int INF = 1e9 + 7; 
struct Edge {
    int to;
    int cost;
};

struct Node {
    int id;
    int dist;
    bool operator<(const Node& other) const {
        return dist > other.dist;
    }
};

vector<vector<Edge>> graph;
vector<int> d;
vector<bool> vis;

int dijkstra(int n, int k) {
    d.resize(n + 1, INF);
    vis.resize(n + 1, false);
    priority_queue<Node> pq;
    
    pq.push({1, 0});
    d[1] = 0;
    
    while (!pq.empty()) {
        Node node = pq.top();
        pq.pop();
        int u = node.id;
        
        if (vis[u]) continue;
        vis[u] = true;
        
        for (auto edge : graph[u]) {
            int v = edge.to;
            int w = edge.cost;
            
            int currentTime = node.dist; 
            int wait = currentTime % (2 * k) >= k ? 2 * k - currentTime % (2 * k) : 0; // 等待时间
            
            if (d[v] > d[u] + w + wait) {
                d[v] = d[u] + w + wait;
                pq.push({v, d[v]});
            }
        }
    }
    
    return d[n];
}

signed main() {
    int n, k, m;
    cin >> n >> k >> m;
    
    graph.resize(n + 1);
    
    for (int i = 0; i < m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        
        graph[u].push_back({v, w});
        graph[v].push_back({u, w}); 
    }
    
    int minTime = dijkstra(n, k);
    
    cout << minTime << endl;
    
    return 0;
}

B

首先不难想到直接最短路计算, 记 \(f(i,j)\) 表示跳到 \((i,j)\) 最少使用的体力。 那么转移就是枚举上一个位置然后加上曼哈顿距离求最小值。

考虑优化,我们注意到如果转移都在左上的话坐标正负的贡献是固定的, 所以可以使用数据结构维护。先按照一维扫描线,另一维可以使用线段树或者树状数组维护前缀/后缀最小值。

对于四个象限分别计算一次即可。

时间复杂度 \(O(n^2 \log n)\)

点击查看代码
#include<bits/stdc++.h>
#define pii pair<int, int>
#define fr first
#define sc second
#define int long long
using namespace std;
inline int rd(void){
	int s=0, f=1; char c=getchar();
	while(c<'0' || c>'9') {if(c=='-') f=0; c=getchar();}
	while(c>='0' && c<='9') {s=s*10+c-'0'; c=getchar();}
	return f? s:-s;
}
const int N=505, INF=1e9;
int T;
int n, k, a[N][N], mxa, f[N*N];
vector<pii> vec[N*N];
bool vis[N*N];

inline pii trans(pii pos, int type){// 00 01 10 11
	return {((type&1)? n-pos.fr+1:pos.fr) , (((type>>1)&1)? n-pos.sc+1:pos.sc)};
}

struct Bit{
	int c[N][N];
	void Reset(){//多测重置 
		memset(c, 63, sizeof(c));
	}
	inline void upd(pii pos, int val){//val!=INF 正常更新 val==INF 重置性更新 
		for(int i=pos.fr; i<=n; i+=(i&-i)){
			for(int j=pos.sc; j<=n; j+=(j&-j)){
				if(val!=INF) c[i][j]=min(c[i][j], val);
				else c[i][j]=INF;
			}
		}
	}
	inline int que(pii pos){
		int re=INF;
		for(int i=pos.fr; i; i-=(i&-i)){
			for(int j=pos.sc; j; j-=(j&-j)){
				re=min(re, c[i][j]);
			}
		}
		return re;
	}
} bt[4];

signed main(){
//	freopen("sample_2.in", "r", stdin);
	for(int i=0; i<4; i++) bt[i].Reset();
	T=rd();
	while(T--){
		n=rd(), k=rd();
		for(int i=1; i<=n; i++){
			for(int j=1; j<=n; j++){
				a[i][j]=rd();
				vec[a[i][j]].push_back({i, j});
				mxa=max(mxa, a[i][j]);
				vis[a[i][j]]=1;
			}
		}
		for(pii pos:vec[1]){
			for(int i=0; i<4; i++){
				pii tpos=trans(pos, i);
				bt[i].upd(tpos, -tpos.fr-tpos.sc);
			}
		}
		int ans=INF;
		bool flag=1;
		for(int i=1; i<=k; i++) flag&=(vis[i]!=0);
		
		if(flag)
		for(int step=2; step<=k; step++){
			for(int i=0; i<vec[step].size(); i++){
				int cur=INF;
				for(int j=0; j<4; j++){
					pii tpos=trans(vec[step][i], j);
					cur=min(cur, tpos.fr+tpos.sc+bt[j].que(tpos));
				}
				f[i]=cur;
			}
			//统计答案 
			if(step==k){
				for(int i=0; i<vec[step].size(); i++) ans=min(ans, f[i]);
				break;
			}
			for(pii pos:vec[step-1]){
				for(int i=0; i<4; i++) bt[i].upd(trans(pos, i), INF);
			}
			for(int i=0; i<vec[step].size(); i++){
				for(int j=0; j<4; j++){
					pii tpos=trans(vec[step][i], j);
					bt[j].upd(tpos, f[i]-tpos.fr-tpos.sc);
				}
			}
		}
		
		printf("%lld\n", flag? ans:-1);
		
		//reset
		for(int i=1; i<=mxa; i++) vec[i].clear();
		mxa=0; for(int i=0; i<4; i++) bt[i].Reset();
		memset(vis, 0, sizeof(vis));
		//
	}
	return 0;
}

C

首先每个数只有质因子有用,所以我们只需要保留每个数质因子的乘积即可。

那么通过简单的容斥或莫比乌斯反演我们可以将我们要求的 \(\sum_{i,j}[gcd(v_i,v_j)=1]\) 转化为 \(\sum_{g}\mu(g) \sum_{i,j} [g|v_i,g|v_j]\)

那么考虑假设我们要在路径中加入一个点或者删除一个点, 我们最多只需要枚举8个不同的\(g\) 并计算当前路径中有多少数是 \(g\) 的倍数,也就是说插入和删除的维护和更新可以在 \(O(1)\) 完成。

假设树的形态是一条链且我们可以快速的进行插入和删除,那么我们直接使用序列莫队的技巧即可。

对于一般的情况我们可以使用树上莫队的技巧,首先对树跑一边dfs, 在进入每个点(开始位置)和走出每个点(结束位置)的时刻同时记录, 这样就得到了一个长度为 \(2n\) 的序列,序列上的相邻元素在树上也相邻。注意到如果要统计树上的一条路径 \((u,v)\) 那么我们只需要将 \(u\) 的结束位置到 \(v\) 的开始位置对应序列的区间取出, 忽略出现两次的元素, 剩余的出现正好一次的元素再加上lca就是路径上的点集。 注意如果 \(u\)\(v\) 的祖先需要特判。

于是就将树上莫队转化成了序列莫队问题求解。

最终时间复杂度 \(O(m\sqrt n)\)

点击查看代码
#include <bits/stdc++.h>

using namespace std;

const int N = 2.5e4 + 5, M = 5e4 + 10, W = 1e7;
int n, q, val[N], h[N], e[M], ne[M], idx;
inline void add(int a, int b) {e[idx] = b, ne[idx] = h[a], h[a] = idx ++; }

vector<int> primes; int d[W + 5];
inline void Sieve() {
	d[1] = 1;
	for(int i = 2; i <= W; i ++) {
		if(!d[i]) {d[i] = i; primes.emplace_back(i); }
		for(auto p : primes) {
			int j = i * p; if(j > W) break;
			d[j] = p; if(i % p == 0) break;
		}
	}
}

int fa[N], dep[N], siz[N], hson[N], top[N], in[N], out[N], euler[2 * N], euler_clock;
void dfs(int u, int f) {
	fa[u] = f, dep[u] = dep[f] + 1, siz[u] = 1, hson[u] = 0, in[u] = ++euler_clock, euler[in[u]] = u;
	for(int i = h[u]; i != -1; i = ne[i]) {
		int v = e[i]; if(v == f) continue;
		dfs(v, u); siz[u] += siz[v]; if(siz[v] > siz[hson[u]]) hson[u] = v;
	}
	out[u] = ++euler_clock; euler[out[u]] = u;
}
void hld(int u, int topf) {
	top[u] = topf; if(!hson[u]) return;
	hld(hson[u], topf);
	for(int i = h[u]; i != -1; i = ne[i]) {
		int v = e[i]; if(v == fa[u] || v == hson[u]) continue;
		hld(v, v);
	}
}
inline int lca(int u, int v) {
	while(top[u] != top[v]) {
		if(dep[top[u]] < dep[top[v]]) swap(u, v);
		u = fa[top[u]];
	}
	return dep[u] < dep[v] ? u : v;
}

int B, block[M];
struct Query_Node {
	int u, v, l, r, flag, id;
	Query_Node(int _u, int _v, int _l, int _r, int _flag, int _id) :
		u(_u), v(_v), l(_l), r(_r), flag(_flag), id(_id) {}
	bool operator < (const Query_Node &rhs) const {return block[l] == block[rhs.l] ? r < rhs.r : l < rhs.l; }
};
vector<Query_Node> Q;

vector<int> ds[N][4];
inline void init() {
	B = sqrt(2 * n);
	for(int i = 1; i <= 2 * n; i ++) block[i] = i / B;
	for(int i = 1; i <= n; i ++) {
		int x = val[i]; while(x > 1) {int D = d[x]; ds[i][1].emplace_back(D); while(x % D == 0) x /= D; }
		for(int j = 0; j < ds[i][1].size(); j ++)
			for(int k = j + 1; k < ds[i][1].size(); k ++)
				ds[i][2].emplace_back(ds[i][1][j] * ds[i][1][k]);
		if(ds[i][1].size() == 3) ds[i][3].emplace_back(ds[i][1][0] * ds[i][1][1] * ds[i][1][2]);
	}
}

int cnt[N], Map[W]; long long ans[N], res;
inline void Move(int u, int type) {
	for(auto x : ds[u][type]) {
		if(!cnt[u]) res += Map[x] ++;
		else res -= --Map[x];
	}
	cnt[u] ^= 1;
}
inline void solve() {
	for(int i = 1; i <= 3; i ++) {
		int L = 1, R = 0;
		for(auto &qy : Q) {
			int u = qy.u, v = qy.v, l = qy.l, r = qy.r, flag = qy.flag, id = qy.id;
			while(L > l) Move(euler[--L], i);
			while(R < r) Move(euler[++R], i);
			while(L < l) Move(euler[L ++], i);
			while(R > r) Move(euler[R --], i);

			if(flag) Move(lca(u, v), i);
			ans[id] += ((i & 1) ? -1 : 1) * res;
			if(flag) Move(lca(u, v), i);
		}
		memset(cnt, 0, sizeof cnt), memset(Map, 0, sizeof Map), res = 0;
	}
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0);
	memset(h, -1, sizeof h), Sieve();

	cin >> n >> q;
	for(int i = 1; i <= n; i ++) cin >> val[i];
	for(int i = 1; i < n; i ++) {
		int a, b; cin >> a >> b;
		add(a, b), add(b, a);
	}

	dfs(1, 0), hld(1, 1);
	init();

	for(int i = 1; i <= q; i ++) {
		int u, v; cin >> u >> v; if(dep[u] > dep[v]) swap(u, v); int l = lca(u, v);
		if(l == u) Q.emplace_back(Query_Node(u, v, in[u], in[v], 0, i));
		else Q.emplace_back(Query_Node(u, v, out[u], in[v], 1, i));
		long long len = dep[u] + dep[v] - 2 * dep[l] + 1; ans[i] = (len * (len - 1)) / 2;
	}

	sort(Q.begin(), Q.end()), solve();
	for(int i = 1; i <= q; i ++) cout << ans[i] << '\n';

	return 0;
}

D

首先我们注意到任意两个秘密据点的路径中点是重要的,为了让这个中点不出现在边上一个常见的技巧就是在每条边上新建一个点。

然后我们先考虑在扩建了树之后每个 \(M\) 值的改变,原先树上的点 \(M\) 值会变成两倍。 考虑原图中的边 \((u,v)\) 中间新建的点 \(x\) 那么首先必须有 \(|M_u-M_v|\le 2\) 如果 \(|M_u-M_v|=2\) 那么 \(M_x=(M_u+M_v)/2\). 如果 \(M_u=M_v\) 这时候这条边中点 \(x\) 一定是某两个秘密据点的中点,也就是说这种边最多只有3个。这些 \(M_x=M_u \pm 1\), 可以直接暴力枚举。

接着我们考虑 \(u_1,u_2,u_3\) 的位置关系,它们三个点一定有一个中心 \(u\), 不妨记 \(D_i=d(u,u_i)\), 并且假设 \(D_1\le D_2\le D_3\).

我们按照 \(M\) 值从大到小给边定向, 如果一个点没有出度则称为汇点。 通过观察: 当 \(D_1<D_2\le D_3\) 的时候有且只有两个汇点。 当 \(D_1=D_2\le D_3\) 的时候有且只有一个汇点。计算出汇点的个数分类讨论。

一个汇点的情况: 此时汇点一定是三个点的中心, 设汇点为 \(r\), 且 \(D_1=D_2=M_r,D_3>M_r\) 我们只需要通过简单的背包的\(dp\) 计数即可。

两个汇点的情况: 设两个汇点为 \(r_1,r_2\). 我们会发现 \(u_1,u_2,u_3\) 的点到 \(r_1,r_2\) 的距离已经全部确定了,只要分别独立的满足这些距离, 将方案数相乘即可,两次 dfs 即可。

最终时间复杂度 \(O(n)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
#define int ll
#define rng(i,a,b) for(int i=int(a);i<int(b);i++)
#define rep(i,b) rng(i,0,b)
#define gnr(i,a,b) for(int i=int(b)-1;i>=int(a);i--)
#define per(i,b) gnr(i,0,b)
#define pb push_back
#define eb emplace_back
#define a first
#define b second
#define bg begin()
#define ed end()
#define all(x) x.bg,x.ed
#define si(x) int(x.size())
template<class t> using vc=vector<t>;
template<class t> using vvc=vc<vc<t>>;
using pi=pair<int,int>;
using vi=vc<int>;
#define mp make_pair
ll read(){
	ll i;
	cin>>i;
	return i;
}
vi readvi(int n,int off=0){
	vi v(n);
	rep(i,n)v[i]=read()+off;
	return v;
}
bool inc(int a,int b,int c){
	return a<=b&&b<=c;
}
template<class E>
struct doubling{
	const vvc<E>&g;
	const int n,h;
	int cnt;
	vvc<int> par;
	vi dep,in,out;
	void dfs(int v,int p,int d){
		par[0][v]=p;
		dep[v]=d;
		in[v]=cnt++;
		for(auto e:g[v])if(e!=p)
			dfs(e,v,d+1);
		out[v]=cnt;
	}
	doubling(const vvc<E>&gg,int r):g(gg),n(g.size()),h(log2(n)+1),
		cnt(0),par(h,vi(n,-1)),dep(n),in(n),out(n){
		dfs(r,-1,0);
		rng(i,1,h){
			rep(j,n)
				if(par[i-1][j]!=-1)
					par[i][j]=par[i-1][par[i-1][j]];
		}
	}
	int lca(int a,int b){
		if(dep[a]>dep[b])swap(a,b);
		int w=dep[b]-dep[a];
		rep(i,h)if(w&1<<i)
			b=par[i][b];
		if(a==b)return a;
		per(i,h){
			int x=par[i][a],y=par[i][b];
			if(x!=y)tie(a,b)=pi(x,y);
		}
		return par[0][a];
	}
	int len(int a,int b){
		return dep[a]+dep[b]-dep[lca(a,b)]*2;
	}
	int jump(int a,int b,int d){
		int c=lca(a,b);
		int w=dep[a]+dep[b]-dep[c]*2;
		if(d<=dep[a]-dep[c]){
			rep(i,h)if(d&1<<i)
				a=par[i][a];
			return a;
		}else{
			d=w-d;
			rep(i,h)if(d&1<<i)
				b=par[i][b];
			return b;
		}
	}
};

using A=array<int,3>;
vi getvals(int n,const vc<pi>&es,A rs){
	vvc<int> t(n);
	rep(i,n-1){
		t[es[i].a].pb(es[i].b);
		t[es[i].b].pb(es[i].a);
	}
	auto getdist=[&](int r){
		vi dist(n,-1);
		queue<int> q;
		auto reach=[&](int v,int d){
			if(dist[v]==-1){
				dist[v]=d;
				q.push(v);
			}
		};
		reach(r,0);
		while(si(q)){
			int v=q.front();q.pop();
			for(auto to:t[v])
				reach(to,dist[v]+1);
		}
		return dist;
	};
	vc<A> as(n);
	rep(k,3){
		vi d=getdist(rs[k]);
		rep(i,n)as[i][k]=d[i];
	}
	vi res(n);
	rep(i,n){
		sort(all(as[i]));
		res[i]=as[i][1];
	}
	return res;
}

bool dbg=false;

void solve(){
	int n;
	cin>>n;
	vc<pi> es;
	rep(i,n-1) es.eb(i+1,read()-1);
	vi val;
	val=readvi(n);
	vi valraw=val;
	val.resize(n+n-1);
	vvc<int> t(n+n-1);

	auto ae=[&](int a,int b){
		t[a].pb(b);
		t[b].pb(a);
	};
	vi waf;
	rep(i,n-1){
		int a,b;tie(a,b)=es[i];
		int d=abs(val[a]-val[b]);
		if(d>1){
			cout<<0<<endl;
			return;
		}
		if(d==0){
			waf.pb(i);
		}else{
			val[n+i]=val[a]+val[b];
		}
		ae(a,n+i);
		ae(b,n+i);
	}
	rep(i,n)val[i]*=2;
	
	if(si(waf)>5){
		cout<<0<<endl;
		return;
	}
	n=n*2-1;
	
	int ans=0;
	
	doubling<int> ysp(t,0);
	
	rep(bit,1<<si(waf)){
		rep(i,si(waf)){
			int x=waf[i];
			int a,b;tie(a,b)=es[x];
			if(bit&1<<i){
				val[(n+1)/2+x]=val[a]-1;
			}else{
				val[(n+1)/2+x]=val[a]+1;
			}
		}
		
		int pa=-1,pb=-1;
		
		bool ng=false;
		
		rep(i,n){
			bool mn=true;
			for(auto j:t[i]){
				if(val[j]<val[i])mn=false;
			}
			if(mn){
				if(pa==-1){
					pa=i;
				}else if(pb==-1){
					pb=i;
				}else{
					ng=true;
				}
			}
		}
		
		if(!ng){
			int tar;
			auto dfs=[&](auto self,int v,int p,int d)->pi{
				int a=0,b=0;
				if(v<=n/2){
					if(tar==d)a++;
					else if(tar<d)b++;
				}
				for(auto to:t[v]){
					if(to!=p){
						int x,y;tie(x,y)=self(self,to,v,d+1);
						a+=x;
						b+=y;
					}
				}
				return pi(a,b);
			};
			if(pb==-1){
				tar=val[pa];
				assert(tar>0);
				int dp[4][2]{};
				dp[0][0]=1;
				for(auto v:t[pa]){
					int a,b;tie(a,b)=dfs(dfs,v,pa,1);
					per(x,4)per(y,2){
						if(x+1<4)dp[x+1][y]+=dp[x][y]*a;
						if(y+1<2)dp[x][y+1]+=dp[x][y]*b;
					}
				}
				ans+=dp[3][0];
				ans+=dp[2][1];
			}else{
				int len=ysp.len(pa,pb);
				int sum=val[pa]+val[pb];
				if(sum>=len&&(sum-len)%2==0){
					int z=(sum-len)/2;
					int k=val[pa]-z;
					if(inc(1,k,len-1)){
						int w=1;
						rep(_,2){
							tar=val[pa];
							int x=dfs(dfs,pa,ysp.jump(pa,pb,1),0).a;
							w*=x;
							swap(pa,pb);
						}
						{
							int pc=ysp.jump(pa,pb,k);
							int nga=ysp.jump(pc,pa,1);
							int ngb=ysp.jump(pc,pb,1);
							int x=0;
							tar=z;
							if(tar==0){
								x=1;
							}else{
								for(auto to:t[pc]){
									if(to!=nga&&to!=ngb){
										x+=dfs(dfs,to,pc,1).a;
									}
								}
							}
							w*=x;
						}
						ans+=w;
					}
				}
			}
		}
	}
	cout<<ans<<endl;
}

signed main(){
	cin.tie(0);
	ios::sync_with_stdio(0);
	int T;cin>>T;
	while (T--){
		//cerr<<123<<endl;
		solve();
	}
	return 0;
}

Day11

70+0+0+0=70

排名:43/58

评价:D+

A

题目

小 A 有 \(n\) 个球,每个球有两个属性 \(a_i,b_i\)
小 A 想从他的这些球中选出一些来,假设选的球的编号为 \(p_1,p_2,\dots,p_k\),小 A 想要最大化 \(\sum\limits_{i=1}^{k} b_{p_i}-(\max\limits_{i=1}^k a_{p_i}-\min\limits_{i=1}^k a_{p_i})\)
小 A 想知道她想要最大化的值最大能有多少。

第一行一个整数 \(n\)
接下来 \(n\) 行每行两个整数,第 \(i+1\) 行表示 \(a_i,b_i\)

输出一行一个整数,表示答案。

思路

考虑按照\(A\)排序,选择的必然是个区间。令\(preb_i\)表示\(b\)的前缀和数组,那么区间\([l,r]\)的价值为\(preb_r-preb_{l-1}-(a_r-a_l)\)

枚举\(r\),记录最小的\(preb_{l-1}-a_l\)
时间复杂度就是 \(O(n)\)

代码

点击查看代码
/*
最大值和最小值确定之后,能选的都选一定最优.
枚举最小值,线段树维护 $\sum b - \max a$ 的最值。
*/
#include <bits/stdc++.h>

using namespace std;
typedef pair<long long, int> node;

const int N = 5e5 + 5;
int n; node A[N]; long long B[N];

namespace SegmentTree {
#define lc (u << 1)
#define rc (u << 1 | 1)
    long long C[N << 2];
    void Build(int u, int l, int r) {memset(C, -0x3f, sizeof C); }
    void Modify(int u, int l, int r, int pos, long long val) {
        if(l == r) {C[u] = val; return; }
        int mid = (l + r) >> 1;
        if(pos <= mid) Modify(lc, l, mid, pos, val);
        else Modify(rc, mid + 1, r, pos, val);
        C[u] = max(C[lc], C[rc]);
    }
    long long Ask() {return C[1]; }
#undef lc
#undef rc
}

int main() {
    ios::sync_with_stdio(false), cin.tie(0);

    cin >> n; SegmentTree::Build(1, 1, n);
    for(int i = 1; i <= n; i ++) cin >> A[i].first >> A[i].second;
    sort(A + 1, A + n + 1, greater<node>());
    // for(int i = 1; i <= n; i ++) cerr << A[i].first << ' ' << A[i].second << '\n';
    for(int i = 1; i <= n; i ++) B[i] = B[i - 1] + A[i].second;
    
    long long ans = 0, sumb = 0;
    for(int i = 1; i <= n; i ++) {
        sumb += A[i].second;
        SegmentTree::Modify(1, 1, n, i, -B[i - 1] - A[i].first);
        ans = max(ans, SegmentTree::Ask() + sumb + A[i].first);
    }
    cout << ans << '\n';

    return 0;
}

B

题目

对于一个长度为 \(n\) 的排列 \(p\),我们认为一个位置 \(i\ (2\le i\le n-1)\) 是“顶”,当且仅当 \(p_{i-1}<p_i\)\(p_i>p_{i+1}\)
小 B 想知道所有长度为 \(n\) 的排列,有多少个恰好有 \(k\) 个位置是“顶”。
答案对 \(10^9+7\) 取模。

思路

\(dp_{i,j}\)表示长度为\(i\)\(j\)个顶的排列个数。

考虑每次往长度为\(i\)的排列里插入\(i+1\),那么如果插入在了原本定的旁边,这个时候顶的个数不变,否则顶的个数,否则顶的个数就会增加1,即:

\[dp_{i,j}=dp{i-1,j} \times (2 \times j+2)+dp_{i-1,j-1} \times (i-2( \timesj)) \]

时间复杂度是 \(O(n^2)\)

代码

点击查看代码
#include <bits/stdc++.h>

using namespace std;

const int N = 5005, MOD = 1e9 + 7;
int n, k; long long f[N][N];

int main() {
	ios::sync_with_stdio(false), cin.tie(0);

	cin >> n >> k; f[1][0] = 1;
	for(int i = 2; i <= n; i ++)
		for(int j = 0; j <= k; j ++) {
			f[i][j] = f[i - 1][j] * ((j * 2) + 2);
			if(j) f[i][j] += f[i - 1][j - 1] * (i - (j - 1) * 2 - 2);
			f[i][j] %= MOD;
		}
	cout << f[n][k] << '\n';

	return 0;
}

C

题目

二维平面上有 \(n\) 个点。
小 C 对折线很感兴趣,所以他想求出有多少个这 \(n\) 个点的非空子集能画出“闪电”。
形式化地,如果一个点集 \(\{(x_1, y_1), \dots ,(x_k, y_k)\}\) 能画出“闪电”,当且仅当把它们按照 \(y_i\) 降序排序后满足:

  • \(\forall j\in [3,k], x_{j-2}<x_j<x_{j-1} \ \text{or}\ x_{j-1}<x_j<x_{j-2}\)
    你只需要告诉他答案对 \(10^9 + 7\) 取模后的结果。

第一行一个整数 \(n\),表示点的个数
接下来 \(n\) 行,每行两个整数 \(x_i, y_i\),表示第 \(i\) 个点的坐标。

一行一个整数表示答案。

思路

将点按照\(X_i\)排序,

\(dp{i,0/1}\)表示以第\(i\)个点为顶端接下来向左或者向右的折线方案数。

考虑依次加入\((x_1,y_1),(x_2,y_2) \dots(x_n,y_n)\)然后更新dp数组。

由于新点横坐标最大,所以只可能在折线的第一位或第二位。

转移的时候可以倒叙枚举\(j\):

  • \(y_i > y_j:dp_{i,0} \leftarrow -dp_{i,0}+dp_{j,1}\)

  • \(y_i<y_j:dp_{j,1}\leftarrow dp_{j,1}+dp_{i,0}\)

这样就能做到时间复杂度\(O(n^2)\),空间复杂度\(O(n)\)

代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define pii pair<int,int>
#define fr first
#define sc second
inline int rd(){
	int res=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		res=res*10+(ch-'0');
		ch=getchar();
	}
	return res*f;
}
const int N=6e3+10,mod=1e9+7;
typedef long long ll;
char _b;
int L[N],R[N],n;
ll ans;
struct node{
	int x,y;
	inline bool operator<(const node &o)const{
		return x<o.x;
	}
}p[N];
char _e;
inline void add(int &x,int y){
	x=(ll)(x+y)%mod;
}
signed main(){
	n=rd();
	for(int i=1;i<=n;i++){
		p[i].x=rd();p[i].y=rd();
	}
	sort(p+1,p+1+n);
	for(int i=1;i<=n;i++)R[i]=L[i]=1;
	for(int i=1;i<=n;i++){
		for(int j=i-1;j>=1;j--){
			if(p[i].y<p[j].y){
				add(R[j],L[i]);
			}else{
				add(L[i],R[j]);
			}
		}
	}
	for(int i=1;i<=n;i++){
		ans+=(L[i]+R[i])%mod;
		ans%=mod;
	}
	ans=(ans-n+mod)%mod;
	printf("%lld\n",ans);

	return 0;
}


D

题目

给出一个长度为 \(n\) 的序列 \(a\),有 \(q\) 次询问。
每次询问会给出两个整数 \(l, r\),你需要求 \(\max\limits_{l\le i<j<k\le r,j−i\le k−j}(a_i + a_j + a_k)\)

第一行一个整数 \(n\)
第二行 \(n\) 个整数表示序列 \(a\)
第三行一个整数 \(q\)
接下来 \(q\) 行每行两个整数 \(l, r\)

输出 \(q\)

思路

如有 \(a_i<a_j(i<j)\)存在 \(k\)满足\(i<k<j,a_k>a_i\) ,显然\(a_k,a_j\) 作为前两个更优。
同理,如有 \(a_i>a_j(i<j)\)存在 \(k\)满足 \(i<k<j,a_k>a_j\),显然\(a_i,a_k\) 作为\(a_i,a_k\)前两个更优。
那么有用的前两个的对就只剩下了 \(O(n)\)个,具体为每个数和它左边第一个比他的大的数形成的对,以及每个数和它右边第一个大于等于它的数形成的对。
接着,对于询问,按左端点从大到小排序,在求解的同时加入有用的对。
现在问题变成了,有 \(n\)个标记 \(c_{1 \dots n}\),一开始全为 \(0\),每次操作对\(c\) 的一个后缀取 \(max\),或者询问一个区间 \(c_i\)+a_i$ 的最大值。
线段树维护即可。

代码

点击查看代码
#include <bits/stdc++.h>

using namespace std;

struct Query_Node {
	int l, r, id;
	Query_Node(int _id) {cin >> l >> r; id = _id; }
	bool operator < (const Query_Node &rhs) const {return l < rhs.l; }
};

const int N = 5e5 + 10;
int n, A[N], q; long long ans[N];
vector<Query_Node> Q;

vector<int> R[N];
int Bit[N];
inline void init() {
	vector<int> lsh;
	for(int i = 1; i <= n; i ++) lsh.emplace_back(A[i]);
	sort(lsh.begin(), lsh.end()); lsh.erase(unique(lsh.begin(), lsh.end()), lsh.end());
	static int B[N];
	for(int i = 1; i <= n; i ++) B[i] = lower_bound(lsh.begin(), lsh.end(), A[i]) - lsh.begin() + 1;

	memset(Bit, 0, sizeof Bit);
	for(int i = 1; i <= n; i ++) {
		int x = 0, p = n - B[i]; while(p) x = max(x, Bit[p]), p -= p & -p;
		if(x) R[x].emplace_back(i);
		x = i, p = n - B[i] + 1; while(p <= n) Bit[p] = max(Bit[p], x), p += p & -p;
	}
	memset(Bit, 0x3f, sizeof Bit);
	for(int i = n; i >= 1; i --) {
		int x = 0x3f3f3f3f, p = n - B[i] + 1; while(p) x = min(x, Bit[p]), p -= p & -p;
		if(x != 0x3f3f3f3f) R[i].emplace_back(x);
		x = i, p = n - B[i] + 1; while(p <= n) Bit[p] = min(Bit[p], x), p += p & -p;
	}
}

namespace SegmentTree {
#define lc (u << 1)
#define rc (u << 1 | 1)
	long long Ans[N << 2]; int Val[N << 2], Tag[N << 2];
	inline void maintain(int u) {Ans[u] = max(Ans[lc], Ans[rc]); }
	inline void Add_Tag(int u, int val) {Ans[u] = max(Ans[u], 1ll * Val[u] + val), Tag[u] = max(Tag[u], val); }
	inline void pushdown(int u) {if(!Tag[u]) return; Add_Tag(lc, Tag[u]), Add_Tag(rc, Tag[u]); Tag[u] = 0; }
	void Build(int u, int l, int r) {
		if(l != r) {
			int mid = (l + r) >> 1;
			Build(lc, l, mid), Build(rc, mid + 1, r);
			Val[u] = max(Val[lc], Val[rc]), maintain(u);
		} else Val[u] = A[l];
	}
	void update(int u, int l, int r, int L, int R, int val) {
		if(l >= L && r <= R) {Add_Tag(u, val); return; }
		int mid = (l + r) >> 1; pushdown(u);
		if(mid >= L) update(lc, l, mid, L, R, val);
		if(mid < R) update(rc, mid + 1, r, L, R, val);
		maintain(u);
	}
	long long Ask(int u, int l, int r, int L, int R) {
		if(l >= L && r <= R) return Ans[u];
		int mid = (l + r) >> 1; pushdown(u);
		if(mid >= R) return Ask(lc, l, mid, L, R);
		else if(mid < L) return Ask(rc, mid + 1, r, L, R);
		else return max(Ask(lc, l, mid, L, R), Ask(rc, mid + 1, r, L, R));
	}

#undef lc
#undef rc
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0);

	cin >> n;
	for(int i = 1; i <= n; i ++) cin >> A[i]; 
	SegmentTree::Build(1, 1, n); cin >> q;
	for(int i = 1; i <= q; i ++) 
		Q.emplace_back(Query_Node(i));
	init(); sort(Q.begin(), Q.end());

	static long long tst[N];
	for(int _ = n; _ >= 1; _ --) {
		// for(auto o : R[_]) for(int i = 2 * o - _; i <= n; i ++) tst[i] = max(tst[i], 1ll * A[o] + A[_]);
		// while(!Q.empty() && Q.back().l == _) {
		//	int l = Q.back().l, r = Q.back().r, id = Q.back().id;
		//	for(int i = l; i <= r; i ++) ans[id] = max(ans[id], tst[i] + A[i]);
		//		Q.pop_back();
		// }

		for(auto o : R[_]) 
			if(2 * o - _ <= n) SegmentTree::update(1, 1, n, 2 * o - _, n, A[o] + A[_]);
		while(!Q.empty() && Q.back().l == _) {
			int l = Q.back().l, r = Q.back().r, id = Q.back().id;
			ans[id] = SegmentTree::Ask(1, 1, n, l, r);
			Q.pop_back();
		}
	}
	for(int i = 1; i <= q; i ++) cout << ans[i] << '\n';

	return 0;
}
posted @ 2023-08-13 13:04  Kylin_ZHH  阅读(66)  评论(0)    收藏  举报