博弈(game)
观察到博弈过程中胜负态不会发生改变,那么求出从每个棋子出发能走的最长链,然后背包即可。
复杂度 O ( n m ) O(nm) O(nm)。
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int mod=998244353;
const int N=305;
int n,m,dp[N],in[N],g[N*N],g2[N*N],vis[N];
ll res;
string s;
queue<int>Q;
vector<int>ve[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m>>s;
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v,u--,v--;
if(u>v)swap(u,v);ve[v].pb(u),in[u]++;
}for(int i=0;i<n;i++)if(!in[i])Q.push(i);
while(Q.size()){
int u=Q.front();Q.pop();
for(auto v:ve[u]){
if(--in[v]==0)Q.push(v);
if(s[u]==s[v])dp[v]=max(dp[v],dp[u]+1),vis[v]=1;
else if(s[u]!=s[v]&&!vis[u]&&!dp[u])dp[v]=max(dp[v],1);
}
}g[0]=g2[0]=1;
for(int i=0;i<n;i++){
if(s[i]=='W'){
for(int j=n*n;j>=dp[i];j--){
g[j]=(g[j]+g[j-dp[i]])%mod;
}
}
else{
for(int j=n*n;j>=dp[i];j--){
g2[j]=(g2[j]+g2[j-dp[i]])%mod;
}
}
}
for(int i=1;i<=n*n;i++){
g2[i]=(g2[i-1]+g2[i])%mod;
}for(int i=1;i<=n*n;i++){
res=(res+(ll)g[i]*g2[i-1])%mod;
}cout<<res;
}
排列 (perm)
神仙题。
考虑怎么转化这个性质。等价于,不能存在一个区间,里面同时存在 ( a , b ) , ( b , c ) (a,b),(b,c) (a,b),(b,c)而不存在 ( a , c ) (a,c) (a,c)。
由此可以想到传递闭包:每个区间,如果建一个图,就都必须具有传递性。
然后我们发现,只需要每个前缀都有传递性,每个后缀都有传递性就好了。
不要问我怎么想到的,以及证明,国内谜语人太多了,可以去骂写题解的人
也就是说,任取一个 i i i,设 G i G_i Gi为考虑前 i i i条边组成的图,那么 G i G_i Gi和 G i G_i Gi的补图都有传递性。
有一个很神奇的结论:满足这样条件的 G G G恰好有 n ! n! n!个,这是因为,任取一个排列 p p p,如果 a < b a<b a<b并且 a a a在 p p p中的位置在 b b b的后面,那么连边 ( a , b ) (a,b) (a,b),这样构造出来的图一定是满足传递性的,并且这是一个双射。
然后,最无脑的想法是,因为合法的前缀只有 n ! n! n!个,所以状态数只有 O ( n ! ) O(n!) O(n!),也就是说可以直接把整张图的每条边存起来,然后暴力转移。不过既然我们都知道这样的 G G G和一个排列 p p p构成双射了,那么在 p p p中转移则显得合情合理,一个合法的 G G G加入一条边 ( a , b ) (a,b) (a,b)后仍然合法,当且仅当 G G G对应的排列 p p p中 a a a恰好在 b b b前面的位置,加入边 ( a , b ) (a,b) (a,b)相当于是交换 ( a , b ) (a,b) (a,b)的位置。
复杂度 O ( n ! × n ) O(n!\times n) O(n!×n)。
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
int n,m,fac[11],p[11],vs[11][11];
int A[35],B[35],C[35],D[35],dp[3628805];
int dfs(int x){
int id=0;
for(int i=1;i<=n;i++){
int x=p[i];
for(int j=1;j<i;j++)if(p[j]<p[i])x--;
id+=fac[n-i]*(x-1);
}if(~dp[id])return dp[id];
if(x>n*(n-1)/2)return 1;
dp[id]=0;
for(int i=1;i<=m;i++){
if(vs[C[i]][D[i]]&&!vs[A[i]][B[i]])return 0;
}
for(int i=1;i<n;i++){
if(p[i]<p[i+1]){
vs[p[i]][p[i+1]]=1,swap(p[i],p[i+1]),dp[id]=(dp[id]+dfs(x+1))%mod,swap(p[i],p[i+1]),vs[p[i]][p[i+1]]=0;
}
}return dp[id];
}
int main(){
memset(dp,-1,sizeof dp),cin>>n>>m;fac[0]=1;for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i,p[i]=i;
for(int i=1;i<=m;i++)cin>>A[i]>>B[i]>>C[i]>>D[i];
cout<<dfs(1);
}
子段和 (seg)
直接用数据结构暴力维护就能做到 O ( n 2 log w ) O(n^2\log w) O(n2logw),可以得到 70 p t s 70pts 70pts。
接下来一步应该容易想到:对于 max \max max相同的区间,我们每次要取出 min \min min最小的那一个,对于这个过程,我们可以用一个 set \text{set} set来维护。考虑堆套 set \text{set} set,堆里面每个元素维护区间 max \max max相同的区间的集合,用 set \text{set} set维护这些区间的最小值的位置。这里有一个比较巧妙的想法,就是把堆中的元素存在一个数组里,这样每次从堆中取元素时就不用把它复制一遍了。
复杂度 O ( n log n ) O(n\log n) O(nlogn)。
后来发现题解的思路确实更加聪明。
考虑给定 k k k,怎么求 g ( k ) g(k) g(k)呢,二分答案 w w w,令 a a a为给出序列的前缀和, a 0 = 0 a_0=0 a0=0,每次操作就是对一段后缀 − 1 -1 −1,最后要使得 ∀ i < j , a j − a i ≤ w \forall i<j,a_j-a_i\le w ∀i<j,aj−ai≤w。
直接从前往后贪心,维护一个 lim \text{lim} lim表示后面的 a a a经过操作后都不能超过 lim \text{lim} lim。如果 a i > lim a_i>\text{lim} ai>lim那么就在这里执行 a i − lim a_i-\text{lim} ai−lim次操作,得到新的 a a a。然后令 lim : = min ( lim , a i + w ) \text{lim}:=\min(\text{lim},a_i+w) lim:=min(lim,ai+w)。
正解非常脑洞。考虑直接把所有 w w w的 lim \text{lim} lim一起维护,设这个函数为 L ( w ) L(w) L(w),另外维护 A ( w ) A(w) A(w)表示目前的代价。剩下的就是用数据结构大力乱搞。
先贴一个 70 p t s 70pts 70pts的代码吧。
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int mod=998244353;
int n,p[200005][20],p2[200005][20],Log[200005];
ll a[200005],s[200005],Min[200005][20],Max[200005][20],K,res,inv2=(mod+1)/2;
vector<pair<ll,ll>>v;
int qmin(int l,int r){
int k=Log[r-l+1];
return (Min[l][k]<Min[r-(1<<k)+1][k])?p[l][k]:p[r-(1<<k)+1][k];
}
int qmax(int l,int r){
int k=Log[r-l+1];
return (Max[l][k]>Max[r-(1<<k)+1][k])?p2[l][k]:p2[r-(1<<k)+1][k];
}
struct node{
int l,r;
ll S;
bool operator <(const node &a)const{
return s[qmax(l,r)]-S<s[qmax(a.l,a.r)]-a.S;
}
};
priority_queue<node>q;
vector<node>vec;
ll Sum(ll l,ll r){
l%=mod,r%=mod;
return (r-l+1)*(l+r)%mod*inv2%mod;
}
void calc(ll &K,ll Max,ll x,ll y){
if(K/x>=y){
res+=Sum(Max-y+1,Max)*(x%mod)-y;
res%=mod,K-=x*y;
}
else{
res+=Sum(Max-K/x+1,Max)*(x%mod)-(K/x),res%=mod;
Max-=K/x,K%=x,res+=(K%mod)*(Max%mod),res%=mod,K=0;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;for(int i=2;i<=n;i++)Log[i]=Log[i/2]+1;
for(int i=1;i<=n;i++){
cin>>a[i],s[i]=max(s[i-1]+a[i],0ll),Min[i][0]=Max[i][0]=s[i],p[i][0]=p2[i][0]=i;
}for(int j=1;j<20;j++){
for(int i=1;i<=n-(1<<j)+1;i++){
Min[i][j]=min(Min[i][j-1],Min[i+(1<<j-1)][j-1]),Max[i][j]=max(Max[i][j-1],Max[i+(1<<j-1)][j-1]);
p[i][j]=(Min[i][j]==Min[i][j-1])?p[i][j-1]:p[i+(1<<j-1)][j-1],p2[i][j]=(Max[i][j]==Max[i][j-1])?p2[i][j-1]:p2[i+(1<<j-1)][j-1];
}
}q.push({1,n,0});
cin>>K;
while(q.size()){
ll MAX=s[qmax(q.top().l,q.top().r)]-q.top().S,S2(inf);vec.clear();
if(!MAX)break;
while(q.size()&&s[qmax(q.top().l,q.top().r)]-q.top().S==MAX){
int l=q.top().l,r=q.top().r;ll S=q.top().S;q.pop();
S2=min(S2,s[qmin(l,r)]-S),vec.pb({l,r,S});
}if(q.size())S2=min(S2,MAX-(s[qmax(q.top().l,q.top().r)]-q.top().S));
for(int i=0;i<vec.size();i++){
int l=vec[i].l,r=vec[i].r,p=qmin(l,r);ll S=vec[i].S;
if(s[p]-S-S2==0){
if(p>l)q.push({l,p-1,S+S2});
if(p<r)q.push({p+1,r,S+S2});
}else q.push({l,r,S+S2});
}calc(K,MAX,vec.size(),S2);
if(!K)break;
}
if(K){
for(int i=1;i<=n;i++)a[i]=min(a[i],0ll);
sort(a+1,a+1+n),reverse(a+1,a+1+n);
for(int i=2;i<=n;i++){
calc(K,a[i-1],i-1,a[i-1]-a[i]);
if(!K)break;
}if(K){
calc(K,a[n],n,inf);
}
}
cout<<(res+mod)%mod;
}
正解没调出来。
最后发现被 set \text{set} set阴了。。。。换成 multiset \text{multiset} multiset就过了,我一下午的光阴啊。。。原来 set \text{set} set是按比较函数去重的啊。。。
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int mod=998244353;
int n,m,p[200005][20],p2[200005][20],Log[200005];
ll a[200005],s[200005],Min[200005][20],Max[200005][20],mx[200005],tag[200005],len[200005],K,res,inv2=(mod+1)/2;
struct node2{
int l,r;ll min;
bool operator <(const node2&a)const{
return min<a.min;
}
};
multiset<node2>S[200005];
int qmin(int l,int r){
int k=Log[r-l+1];
return (Min[l][k]<Min[r-(1<<k)+1][k])?p[l][k]:p[r-(1<<k)+1][k];
}
int qmax(int l,int r){
int k=Log[r-l+1];
return (Max[l][k]>Max[r-(1<<k)+1][k])?p2[l][k]:p2[r-(1<<k)+1][k];
}
struct cmp{
bool operator()(int x,int y){
return mx[x]<mx[y];
}
};
priority_queue<int,vector<int>,cmp>q;
ll Sum(ll l,ll r){
l%=mod,r%=mod;
return (r-l+1)*(l+r)%mod*inv2%mod;
}
void calc(ll &K,ll Max,ll x,ll y){
if(K/x>=y){
res+=Sum(Max-y+1,Max)*(x%mod)-y;
res%=mod,K-=x*y;
}
else{
res+=Sum(Max-K/x+1,Max)*(x%mod)-(K/x),res%=mod;
Max-=K/x,K%=x,res+=(K%mod)*(Max%mod),res%=mod,K=0;
}
}
int merge(int x,int y){
if(S[x].size()<S[y].size())swap(x,y);
assert(S[x].size()==len[x]),assert(S[y].size()==len[y]);
assert(x!=y);
len[x]+=len[y];
for(auto z:S[y])S[x].insert({z.l,z.r,z.min-tag[y]+tag[x]});
assert(len[x]==S[x].size());
return x;
}
int main(){
freopen("seg.in","r",stdin);
freopen("seg.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;for(int i=2;i<=n;i++)Log[i]=Log[i/2]+1;
for(int i=1;i<=n;i++){
cin>>a[i],s[i]=max(s[i-1]+a[i],0ll),Min[i][0]=Max[i][0]=s[i],p[i][0]=p2[i][0]=i;
}
for(int j=1;j<20;j++){
for(int i=1;i<=n-(1<<j)+1;i++){
Min[i][j]=min(Min[i][j-1],Min[i+(1<<j-1)][j-1]),Max[i][j]=max(Max[i][j-1],Max[i+(1<<j-1)][j-1]);
p[i][j]=(Min[i][j]==Min[i][j-1])?p[i][j-1]:p[i+(1<<j-1)][j-1],p2[i][j]=(Max[i][j]==Max[i][j-1])?p2[i][j-1]:p2[i+(1<<j-1)][j-1];
}
}S[m=1].insert({1,n,s[qmin(1,n)]}),len[1]=1,mx[1]=s[qmax(1,n)],q.push({1});
cin>>K;
while(q.size()){
int x=q.top();q.pop();
ll D=S[x].begin()->min-tag[x];
if(!mx[x])break;
if(q.size()&&mx[x]-mx[q.top()]<D){
int y=q.top();q.pop();
calc(K,mx[x],len[x],mx[x]-mx[y]),tag[x]+=mx[x]-mx[y],mx[x]=mx[y];
q.push(merge(x,y));
}else{
calc(K,mx[x],len[x],D);
int l=S[x].begin()->l,r=S[x].begin()->r,p=qmin(l,r);S[x].erase(S[x].begin());
len[x]--,tag[x]+=D,mx[x]-=D;
if(l<p){
m++,len[m]=1,tag[m]=0,mx[m]=s[qmax(l,p-1)]-s[p];
S[m].insert({l,p-1,s[qmin(l,p-1)]-s[p]});
q.push(m);
}if(p<r){
m++,len[m]=1,tag[m]=0,mx[m]=s[qmax(p+1,r)]-s[p];
S[m].insert({p+1,r,s[qmin(p+1,r)]-s[p]});
q.push(m);
}
if(S[x].size())q.push(x);
}if(!K)break;
}
if(K){
for(int i=1;i<=n;i++)a[i]=min(a[i],0ll);
sort(a+1,a+1+n),reverse(a+1,a+1+n);
for(int i=2;i<=n;i++){
calc(K,a[i-1],i-1,a[i-1]-a[i]);
if(!K)break;
}if(K){
calc(K,a[n],n,inf);
}
}
cout<<(res+mod)%mod;
}