9.13-CSP-S模拟5
由于某些原因,现在改为发单篇博客的形式
9.13CSP-S模拟5
T1 F
比较水的一眼题,先没看题观察数据范围发现是n方的,读题发现显然可能合法的x只有\(O(n)\)个,就是拿\(a_1\)和所有的b异或一遍就行了,别的x既然\(a_1\)都异或不出来那显然不可能,对于一个数,它异或另一个数能得到x的话,那么异或的那个数显然是唯一的,于是随便拿个map或者01Trie维护一下剩下的值就行了。我开始还以为它要算合法的排列数
点击查看代码
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i)
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;
const int maxn=2e3+10;
int a[maxn],b[maxn];
vector<int>vec,Ans;
int n;
void Check(int x){
map<int,int>Mp;
Rep(i,1,n)Mp[b[i]]++;
Rep(i,1,n){
if(Mp.find(a[i]^x)==Mp.end())return;
if(Mp[a[i]^x]>0)Mp[a[i]^x]--;
else return;
}
Ans.push_back(x);
}
void solve(){
fre(f);
cin>>n;
Rep(i,1,n)cin>>a[i];Rep(i,1,n)cin>>b[i];
Rep(i,1,n)vec.push_back(a[1]^b[i]);
sort(vec.begin(),vec.end());vec.erase(unique(vec.begin(),vec.end()),vec.end());
for(auto it : vec)Check(it);
cout<<Ans.size()<<"\n";
for(auto it : Ans)cout<<it<<"\n";
}
int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }
T2 S
赛时打了个假dp连暴力分都不如。正解就是枚举前边三种颜色各放了几个,下一个位置要放啥转移,放的时候显然是贪心的拿最近的那个算一下距离就行。
点击查看代码
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i)
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;
const int maxn=4e2+2,INF=20051107;
int f[2][maxn][maxn][3];
bool vis[maxn][maxn];
int n;
char s[maxn];
int a[maxn];
int posr[maxn],posg[maxn],posy[maxn];
int cnt[maxn][3];
void solve(){
fre(s20);
cin>>n;cin>>(s+1);
Rep(i,1,n){
if(s[i]=='R')posr[++posr[0]]=i;
if(s[i]=='G')posg[++posg[0]]=i;
if(s[i]=='Y')posy[++posy[0]]=i;
cnt[i][0]=posr[0],cnt[i][1]=posg[0],cnt[i][2]=posy[0];
}
int now=1;memset(f,0x3f,sizeof(f));
f[1][1][0][0]=posr[1]-1;
f[1][0][1][1]=posg[1]-1;
f[1][0][0][2]=posy[1]-1;
for(int i=1;i<n;++i){
memset(f[now^1],0x3f,sizeof(f[now^1]));
for(int x=0;x<=posr[0];++x){
if(x>i)break;
for(int y=0;y<=posg[0];++y){
if(x+y>i)break;
int z=i-x-y;
int dx=posr[x+1]-i-1+max(0,y-cnt[posr[x+1]][1])+max(0,z-cnt[posr[x+1]][2]);
int dy=posg[y+1]-i-1+max(0,x-cnt[posg[y+1]][0])+max(0,z-cnt[posg[y+1]][2]);
int dz=posy[z+1]-i-1+max(0,x-cnt[posy[z+1]][0])+max(0,y-cnt[posy[z+1]][1]);
if(x<posr[0])f[now^1][x+1][y][0]=min({f[now^1][x+1][y][0],f[now][x][y][1]+dx,f[now][x][y][2]+dx});
if(y<posg[0])f[now^1][x][y+1][1]=min({f[now^1][x][y+1][1],f[now][x][y][0]+dy,f[now][x][y][2]+dy});
if(z<posy[0])f[now^1][x][y][2]=min({f[now^1][x][y][2],f[now][x][y][0]+dz,f[now][x][y][1]+dz});
}
}
now^=1;
}
cout<<min({f[now][posr[0]][posg[0]][0],f[now][posr[0]][posg[0]][1],f[now][posr[0]][posg[0]][2]});
}
int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }
T3 Y
所有人都给其后边的人一个球的话,那么相当于没给,所以可以所有人都少给一个球,直到有一个人没有给后边的人球,那么环就可以从此断成链,转移是比较显然的,发现我们断链就是枚举一个起点,从起点推到n,再从1推回这个起点,这两部分内,对于每个i,每次的转移系数都是相同的,于是可以矩阵维护,每次乘一个前后缀矩阵就行了。
点击查看代码
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i)
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;
const int maxn=2e6+10,Mod=1e9+7,inv2=500000004,inv6=166666668;
int n,a[maxn];
ll ans;
//int f[maxn][maxn*10];
#define pre(x) ((x+n-1)%n)
#define nxt(x) ((x+1)%n)
struct Matrix{
static const int N=2;
int a[3][3];
Matrix(){memset(a,0,sizeof(a));}
void Clear(){memset(a,0,sizeof(a));}
void Init(){Rep(i,1,N)a[i][i]=1;}
Matrix operator*(const Matrix &x)const{
Matrix z;
for(int i=1;i<=N;i++){
for(int k=1;k<=N;k++){
int r=a[i][k];for(int j=1;j<=N;j++)z.a[i][j]=(1LL*x.a[k][j]*r%Mod+z.a[i][j])%Mod;
}
}
return z;
}
}Mu[maxn],PreMu[maxn],Beg[maxn];
void solve(){
cin>>n;
Rep(i,1,n)cin>>a[i];
reverse(a+1,a+n+1);
Rep(i,1,n)a[i+n]=a[i];
Rep(i,1,n){
Mu[i].a[1][1]=1LL*a[i]*(a[i]+1)%Mod*inv2%Mod;
Mu[i].a[1][2]=((1LL*a[i]*Mu[i].a[1][1]%Mod-1LL*a[i]*(a[i]+1)%Mod*(2LL*a[i]+1)%Mod*inv6%Mod)%Mod+Mod)%Mod;
Mu[i].a[2][1]=(a[i]+1);
Mu[i].a[2][2]=Mu[i].a[1][1];
Mu[i+n].a[1][1]=(1LL*a[i]*a[i]%Mod-Mu[i].a[1][1]+Mod)%Mod;
Mu[i+n].a[1][2]=Mu[i].a[1][2];
Mu[i+n].a[2][1]=a[i];
Mu[i+n].a[2][2]=Mu[i].a[1][1];
Beg[i].a[1][1]=1LL*(a[i])*(a[i]+1)%Mod*inv2%Mod;
Beg[i].a[1][2]=Mu[i].a[1][2];
}
Beg[n+1]=Beg[1];
PreMu[n]=Mu[n];
Dwn(i,n-1,1)PreMu[i]=Mu[i]*PreMu[i+1];
PreMu[n+1]=Mu[n+1];
Rep(i,2,n)PreMu[i+n]=PreMu[i+n-1]*Mu[i+n];
Rep(s,1,n){
int i=s+1;
Matrix Ans;Ans.Init();
if(i==n+1){
Rep(j,1,n-1)Ans=Ans*Mu[j+n];
ans=(ans+1LL*Ans.a[1][1]*a[s]%Mod+Ans.a[1][2])%Mod;
continue;
}
Ans=Ans*PreMu[i];
//Ans=Ans*PreMu[s+n-1];
if(s>1)Ans=Ans*PreMu[s+n-1];
ans=(ans+1LL*Ans.a[1][1]*a[s]%Mod+Ans.a[1][2])%Mod;
// cerr<<ans<<"\n";
}
/* Rep(s,1,n){
memset(f,0,sizeof(f));
int i=s+1;
Rep(j,0,a[i])f[i][j]=a[i]-j;
++i;
for(;i<=n;++i)Rep(j,0,a[i])Rep(k,0,a[i-1])f[i][j]=(f[i][j]+1LL*(a[i]+k-j)*f[i-1][k])%Mod;
if(i<=n)i=n+1;
for(;i<s+n;++i){
Rep(j,1,a[i])Rep(k,0,a[i-1])f[i][j]=(f[i][j]+1LL*(a[i]+k-j)*f[i-1][k])%Mod;
}
i=s+n-1;
Rep(j,0,a[i])ans=(ans+1LL*f[i][j]*(a[s]+j)%Mod)%Mod;
}
*/
cout<<ans<<"\n";
}
int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }
T4 O
由于数据很水所以赛时有错误做法过了,于是现在我把错误做法卡掉了。
正解:
我们考虑维护增量,将每一个位置与前边第一个大于它的位置连边,边的权值为大减小,那么我们可以得到一个森林,发现时间线每增加一,就是在树上将每条边的权值向下传递一次,或者可以理解为每条边对应着一个一次函数,这个一次函数在某个位置,并且有限制的定义域。一条边能贡献的位置一定是连续的一段,我们把每条边的贡献离线下来,然后按照时间轴扫。
设一条边的贡献为一个四元组的形式\((p,t,te,d)\)表示这条边从第p个位置,第t秒开始 产生贡献,第te秒时就没有了贡献。于是一次查询实际上就是查询所有的边对查询的位置造成的贡献,考虑把询问差分成求\(l-1\)和\(r\)两个前缀,这样就少了一个边界限制。于是对于一个询问\((x,i)\),即询问第i秒时x位置的前缀和,一条边的贡献就是
(目前这个式子是不严谨的,后面会说一点细节)
式子的意义就是考虑当前这条边已经扩展到了哪个位置,终点与起点作差算出来扩展了多少个位置,取min是去掉一些不合法的边。
首先,这个式子只对还为终止贡献的边有效,也就是te>当前时间的边,如果一条边已经终止贡献,那么其的贡献形式就变成了一个常函数,直接把它区间加到序列上就行。
其次,对于上边这个式子,我们很完美的把不合法的部分去掉,但是左边的min是终点,右边的min是起点,对于合法的边,我们算区间长度时并没有加一,于是需要特殊考虑一下。
对于上边这个式子,我们把左边的min里的i提出来,变成
这样询问的时间就与边无关了,我们就可以静态的维护一些东西。由于取min并不太好搞,于是我们考虑分情况讨论min到底取了谁,用两棵线段树维护\((p-t)*d\) , \(p*d\) ,线段树的下标就分别是\(p\)和\(p-t\),然后对于两种下标,再开两棵线段树维护每个位置上的\(d\),然后我们就可以讨论min取哪一项,然后取出对应的树上的信息算就行,注意当一条边合法的时候,还需要维护d的下标为\(p-t\)那棵树的合法的那一部分再加一次,也就是算区间长度时的那个加一。纯文字描述比较乏力,不懂可以看代码。
点击查看代码
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i)
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
#define int ll
using namespace std;
const int maxn=5e5+10,LN=-maxn+8,RN=maxn-8,INF=1e9+20051107;
#define gc if(++ip==ie)fread(ip=buf,1,SZ,stdin)
const int SZ=1<<21;char buf[SZ],*ie=buf+SZ,*ip=ie-1;
inline int read(){ gc;while(*ip<'-')gc; bool f=*ip=='-';if(f)gc; int x=*ip&15;gc; while(*ip>'-'){x*=10;x+=*ip&15;gc;} return f ? -x : x; }
struct Seg{
ll tr[maxn<<2],lazy[maxn<<2];
void Pushup(int rt){tr[rt]=tr[rt<<1]+tr[rt<<1|1];}
void Update(int rt,int l,int r,ll w){ tr[rt]+=(r-l+1)*w;lazy[rt]+=w; }
void Pushdown(int rt,int l,int r){
int mid=(l+r)>>1;
Update(rt<<1,l,mid,lazy[rt]),Update(rt<<1|1,mid+1,r,lazy[rt]),lazy[rt]=0;
}
void Modify(int rt,int l,int r,int s,int t,ll w){
if(s<=l && t>=r)return Update(rt,l,r,w);
int mid=(l+r)>>1;
if(lazy[rt])Pushdown(rt,l,r);
if(s<=mid)Modify(rt<<1,l,mid,s,t,w);
if(t>mid)Modify(rt<<1|1,mid+1,r,s,t,w);
Pushup(rt);
}
int Query(int rt,int l,int r,int s,int t){
if(s<=l && t>=r)return tr[rt];
int mid=(l+r)>>1,res=0;
if(lazy[rt])Pushdown(rt,l,r);
if(s<=mid)res+=Query(rt<<1,l,mid,s,t);
if(t>mid)res+=Query(rt<<1|1,mid+1,r,s,t);
return res;
}
}KA,KB,KxA,KxB,C;
int n,q;ll a[maxn>>1],ans[maxn>>1];
int L[maxn>>1],R[maxn>>1];
struct Ver{
int opt,p,t,d;
void Print(){cerr<<" Mod :: "<<opt<<" "<<p<<" "<<t<<" "<<d<<"\n";}
};
vector<pii>Q[maxn>>1];
vector<Ver>Op[maxn>>1];
int st[maxn>>1],top;
void Sol(int Tim){
// cerr<<"Tim :: "<<Tim<<"\n";
for(auto it : Op[Tim]){
// it.Print();
if(it.opt==1){
KxA.Modify(1,LN,RN,it.p,it.p,it.d);
KxB.Modify(1,LN,RN,it.p-it.t,it.p-it.t,it.d);
KA.Modify(1,LN,RN,it.p,it.p,it.p*it.d);
KB.Modify(1,LN,RN,it.p-it.t,it.p-it.t,(it.p-it.t)*it.d);
}else{
KxA.Modify(1,LN,RN,it.p,it.p,-it.d);
KxB.Modify(1,LN,RN,it.p-it.t,it.p-it.t,-it.d);
KA.Modify(1,LN,RN,it.p,it.p,-it.p*it.d);
KB.Modify(1,LN,RN,it.p-it.t,it.p-it.t,-(it.p-it.t)*it.d);
C.Modify(1,1,n,it.p,it.p+Tim-it.t-1,it.d);
}
}
for(auto it : Q[Tim]){
int x=it.fir;if(x==0)continue;
ll res=Tim*KxA.tr[1]+a[x];//+K.Query(1,LN,RN,LN,RN);
res+=KB.Query(1,LN,RN,LN,x-Tim);
res+=KxA.Query(1,LN,RN,LN,x);
res-=KA.Query(1,LN,RN,LN,x);
res+=KxB.Query(1,LN,RN,x-Tim+1,RN)*(x-Tim);
res-=KxA.Query(1,LN,RN,x+1,RN)*x;
res+=C.Query(1,1,n,1,x);
if(it.sec>0)ans[it.sec]+=res;
else ans[-it.sec]-=res;
}
}
void solve(){
n=read(),q=read();
Rep(i,1,n)a[i]=read();
Rep(i,1,n){
while(top && a[i]>=a[st[top]])R[st[top--]]=i;
L[i]=st[top];
st[++top]=i;
}
while(top)R[st[top--]]=n+1;
Rep(i,1,n)if(L[i]){
int t=i-L[i],d=a[L[i]]-a[i],tx=R[i]-i;
Op[t].push_back(Ver{1,i,t,d});
Op[t+tx].push_back(Ver{-1,i,t,d});
}
Rep(i,1,q){
int t,l,r;
t=read(),l=read(),r=read();
Q[t].push_back(mair(l-1,-i));
Q[t].push_back(mair(r,i));
}
Rep(i,1,n)a[i]+=a[i-1];
Rep(i,1,n)Sol(i);
Rep(i,1,q)printf("%lld\n",ans[i]);
}
#undef int
int main (){ return solve(),0; }