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位置的前缀和,一条边的贡献就是

\[d*(\min(p+i-t,x)-\min(p,x)) \]

(目前这个式子是不严谨的,后面会说一点细节)
式子的意义就是考虑当前这条边已经扩展到了哪个位置,终点与起点作差算出来扩展了多少个位置,取min是去掉一些不合法的边。
首先,这个式子只对还为终止贡献的边有效,也就是te>当前时间的边,如果一条边已经终止贡献,那么其的贡献形式就变成了一个常函数,直接把它区间加到序列上就行。
其次,对于上边这个式子,我们很完美的把不合法的部分去掉,但是左边的min是终点,右边的min是起点,对于合法的边,我们算区间长度时并没有加一,于是需要特殊考虑一下。
对于上边这个式子,我们把左边的min里的i提出来,变成

\[d*(i+\min(p-t,x-i)-min(p,x)) \]

这样询问的时间就与边无关了,我们就可以静态的维护一些东西。由于取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; }
posted @ 2022-09-15 21:25  Delov  阅读(61)  评论(1编辑  收藏  举报