还能回到原先吗 再努力也只剩残缺 这名为爱的实验 被等号连接

test44

智乃的差分

看着不是能随随便便处理的,我们分类讨论(

  • \(d<0\)

    \(a\uparrow\) 的差分非负。

  • \(d>0\)

    我们希望 \(a\downarrow\),但是不能保证 \(d_i\) 非正。

    只用考虑 \(d=\max\{a_i\}\),发现如果存在 \(0<a_u<d\) 那么把这个扔到 \(a_i=d\) 前面就可以了。

    如果不存在 \(a_u\) 显然不合法,可以输出 \(a\uparrow\)

  • \(d=0\)

    就是 \(a_{i-1}\neq a_i\),如果存在 \(cnt(a_u)>\lfloor\frac{n}{2}\rfloor\) 显然不合法。

    考虑一种构造方式:先填写奇数位,再填写偶数位。发现只要按照先填写完 \(cnt(v)\) 最大的 \(v\) ,然后再一种一种颜色填写完即可。你可以反证,考虑有一个交了,那么必须严格是 \(v\) 或者只有两种颜色,但是前后者都被天然满足了。

    还要考虑一个 \(a_1\neq 0\),如果 \(2\nmid n\)\(cnt(0)=\frac{n+1}{2}\) 那么不合法,不然 reverse 一下就行了。

#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)

using namespace std;

const int N=100005;

int T, n, d, a[N], b[N], id[N];

void print() {
	cout << "yes\n";
	up(i,1,n) cout << a[i] << ' ';
	cout << '\n';
}

void mian() {
	cin >> n >> d;
	up(i,1,n) cin >> a[i];
	sort(a+1,a+1+n);
	if(d<0) return print();
	if(d>0) {
		if(a[n]!=d) {
			up(i,1,n/2) swap(a[i],a[n-i+1]);
			return print();
		}
		bool flag=0;
		up(i,1,n) if(a[i]>0&&a[i]!=a[n]) flag=1;
		if(flag) print();
		else cout << "no\n";
		return;
	}
	int vmax=0, cnt=0;
	for(int l=1, r=1; l<=n; l=r+1, r=l) {
		while(r<n&&a[r+1]==a[l]) ++r;
		if(r-l+1>cnt) vmax=a[l], cnt=r-l+1;
	}
	if(cnt>(n+1)/2) cout << "no\n";
	else {
		if(!vmax&&n%2==1&&cnt==(n+1)/2) { cout << "no\n"; return; }
		int top=0;
		up(i,1,n) id[i]=(i<=(n+1)/2)?(2*i-1):2*(i-(n+1)/2);
		while(cnt--) b[id[++top]]=vmax;
		up(i,1,n) if(a[i]!=vmax) b[id[++top]]=a[i];
		if(b[1]) up(i,1,n) a[i]=b[i];
		else up(i,1,n) a[i]=b[n-i+1];
		print();
	}
}

signed main() {
	freopen("dif.in","r",stdin);
	freopen("dif.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> T;
	while(T--) mian();
	return 0;
}

牛牛的旅行

\(val\)\(dis\) 的贡献显然独立,分别考虑怎么算。

\(\sum dis\) 可以考虑累加过每一条边的次数 \(siz\times(n-siz)\)

\(\sum val\) 非常经典的可以考虑按照 \(val_i\uparrow\) 加入 \(i\),因为维护连通块后,这样跨越 \(i\) 的边会且仅仅会被 \(val_i\) 贡献。合并一些连通块,现在要考虑经过 \(i\) 的路径条数,这个只关心子树大小,并查集的时候维护一下就好了喵。

#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pii pair<int,int>
#define mp make_pair 
#define pb push_back

using namespace std;

const int N=1000005, P=1e9+7;

int n, val[N], id[N], dsu[N], siz[N], ans;
vector<int> to[N], sav[N];

int get(int x) { return x==dsu[x]?x:dsu[x]=get(dsu[x]); }
void merge(int x,int y) {
	if((x=get(x))==(y=get(y))) return;
	dsu[x]=y, siz[y]+=siz[x];
} 

void dfs(int x,int fad) {
	siz[x]=1;
	for(int y:sav[x]) {
		if(y==fad) continue;
		dfs(y,x), siz[x]+=siz[y];
	}
	(ans-=siz[x]*(n-siz[x])%P*2%P)%=P;
}

signed main() {
	freopen("travel.in","r",stdin);
	freopen("travel.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n;
	up(i,1,n) cin >> val[i];
	up(i,2,n) {
		int u, v;
		cin >> u >> v;
		if(mp(val[u],u)>mp(val[v],v)) to[u].pb(v);
		if(mp(val[u],u)<mp(val[v],v)) to[v].pb(u);
		sav[u].pb(v), sav[v].pb(u);
	}
	dfs(1,0);
	up(i,1,n) id[i]=dsu[i]=i, siz[i]=1;
	sort(id+1,id+1+n,[](int i,int j){return mp(val[i],i)<mp(val[j],j);});
	up(i,1,n) {
		int x=id[i], pl=1;
		for(int y:to[x]) {
			int u=siz[get(y)];
			(pl+=u*u%P)%=P, merge(x,y);
		}
		int u=siz[get(x)]; 
		(ans+=(u*u%P-pl)%P*val[x]%P)%=P;
	}
	cout << (ans%P+P)%P << '\n'; 
	return 0;
}

第K排列

发现 \(n,k\) 都很小,考虑 \(O(nk)\) 暴搜出前 \(k\) 大的串串。发现只差一个估价函数了,直接 \(f[i][c]\) 表示 \(str[i,n]\) 前面拼一个 \(c\) 后能得到的最大权值,好转移的,拿这个剪枝。

#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)

using namespace std;

const int N=1005, inf=1e18;

int n, T, k, val[4][4], f[N][4];
char str[N], now[N];

inline int turn(char c) {
	if(c=='N') return 0;
	if(c=='O') return 1;
	if(c=='I') return 2;
	return 3;
}

void dfs(int x,int tot) {
	if(x>n) {
		if(!--k) {
			up(i,1,n) cout << now[i]; cout << '\n';
			exit(0);
		}
		return;
	}
	for(char c:{'P','O','N','I'}) {
		if(!(str[x]=='?'||str[x]==c)) continue;
		int ran=tot+(x>1?val[turn(now[x-1])][turn(c)]:0);
		if(ran+f[x+1][turn(c)]>=T) now[x]=c, dfs(x+1,ran);
	}
}

signed main() {
	freopen("perm.in","r",stdin);
	freopen("perm.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); 
	cin >> n >> T >> k >> (str+1);
	up(i,0,3) up(j,0,3) cin >> val[i][j];
	dn(i,n,1) {
		up(j,0,3) if(!(str[i]=='?'||turn(str[i])==j)) f[i+1][j]=-inf;
		up(j,0,3) {
			f[i][j]=-inf;
			up(k,0,3) f[i][j]=max(f[i][j],f[i+1][k]+val[j][k]);
		}
	}
	dfs(1,0), cout << -1 << '\n';
	return 0;
}

牛牛的 border

本可都快忘掉 sa 是啥了。

考虑 \([l,r],[p,q]\) 有贡献,那一定在子串 \([l,q]\),所以皮套子串可以扔掉了。

看着不能朴素处理,想办法上点字符串算法,ac 坤看着很诡异,sa 好像可以,答案是 \(\sum_{i\neq j} lcp(s[i,n],s[j,n])\),后缀排序之后贡献是区间 \(\min\),单调栈维护就好了。

#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pii pair<int,int>
#define mp make_pair

using namespace std;

const int N=200005, B=233;

int n, T, ans, len, sa[N], rk[N], sav[N];
int l[N], v[N], top, sum;
ull pw[N], hsh[N];
char str[N];

int lcp(int x,int y) {
	int res=0;
	dn(i,T,0) {
		int len=(1<<i);
		if(x+len-1>n||y+len-1>n) continue;
		ull L=hsh[x+len-1]-hsh[x-1]*pw[len];
		ull R=hsh[y+len-1]-hsh[y-1]*pw[len];
		if(L==R) x+=len, y+=len, res+=len;
	}
	return res;
}

signed main() {
	freopen("border.in","r",stdin);
	freopen("border.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); 
	cin >> n >> (str+1), T=__lg(n), pw[0]=1;
	up(i,1,n) pw[i]=pw[i-1]*B, hsh[i]=hsh[i-1]*B+(str[i]-'a'+1);
	up(i,1,n) sa[i]=i, rk[i]=(str[i]-'a'+1);
	for(len=1; len<=n; len<<=1) {
		sort(sa+1,sa+1+n,[](int l,int r) {
			if(rk[l]!=rk[r]) return rk[l]<rk[r];
			return rk[l+len]<rk[r+len];
		});
		int j=0;
		up(i,1,n) {
			if(i==1||rk[sa[i]]!=rk[sa[i-1]]||rk[sa[i]+len]!=rk[sa[i-1]+len]) ++j;
			sav[sa[i]]=j;
		}
		up(i,1,n) rk[i]=sav[i];
	}
	up(i,2,n) {
		int now=lcp(sa[i-1],sa[i]), lst=i;
		while(top&&v[top]>=now) {
			sum-=v[top]*(v[top]+1)/2*(lst-l[top]);
			lst=l[top--];
		}
		l[++top]=lst, v[top]=now, sum+=now*(now+1)/2*(i-lst+1);
		ans+=sum;
	}
	cout << ans << '\n';
	return 0;
}
posted @ 2025-11-20 15:50  Hypoxia571  阅读(17)  评论(0)    收藏  举报