还能回到原先吗 再努力也只剩残缺 这名为爱的实验 被等号连接
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;
}

浙公网安备 33010602011771号