CSP模拟<反思> (3~15)

csp模拟3#

回文#

区间 dp

暴力的话 可以拿到 3015 的特殊性质

让两个点从两头同时进行转移,发现可以计算出并省掉一维

d[i][j][k] 表示起点到 (i,j) 终点横坐标为 k 的方案数

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int mod=993244853;
char s[502];
int a[505][505];
int f[502][502][502];
signed main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%s",s+1);
		for(int j=1;j<=m;j++){
			a[i][j]=s[j]-'a';
		}
	}
	if(a[1][1]==a[n][m]){
		f[1][1][n]=1;
	}
	int r=(n+m-1)/2;
	for(int i=1;i<=n;i++){
		for(int j=1;j+i<=r+1;j++){
			for(int x=n;x>=i;x--){
				if(f[i][j][x]==0) continue;
				int y=m-i-j-x+2+n;
				if(a[i+1][j]==a[x][y-1]) f[i+1][j][x]=(f[i+1][j][x]+f[i][j][x])%mod;//上 左 
				if(a[i+1][j]==a[x-1][y]) f[i+1][j][x-1]=(f[i+1][j][x-1]+f[i][j][x])%mod;//上 下 
				if(a[i][j+1]==a[x][y-1]) f[i][j+1][x]=(f[i][j+1][x]+f[i][j][x])%mod;//右 左 
				if(a[i][j+1]==a[x-1][y]) f[i][j+1][x-1]=(f[i][j+1][x-1]+f[i][j][x])%mod;//右 下 
			}
		}
	}
	int ans=0;
	if((n+m-1)%2==0){
		for(int i=1;i<=n;i++){
			ans+=f[i][r-i+1][i];ans%=mod;
			ans+=f[i][r-i+1][i+1];ans%=mod;
		}
		cout<<ans<<endl;
		return 0;
	}
	else{
		for(int i=1;i<=n;i++){
			ans+=f[i][(m+n)/2-i+1][i];ans%=mod;
		}
		cout<<ans<<endl;
		return 0;
	}
}
/*
3 4
noip
ffff
pion

10 12
abbcbdbababa
bcccdcdccccb
bcccccccccca
ccccdcdcdcdb
bdcdcccccccd
dcccccccdcdb
bdcdcdcdcccc
accccccccccb
bccccdcdcccb
abababdbcbba

4 5
wwwww
wwwww
wwwww
wwwwa

3 3
abc
bcb
cba


*/

快速排序#

考试的时候没看懂让干啥。

观察它发现,排完序后每个 nan 会到它左边最第一个最大的数哪里,维护一下位置就行了

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5*5+10;
char s[15];
struct asd{
	int id,data;
}b[N];
map<int,int>flat;
int n;
bool amp(asd a,asd b){
	if(a.data==b.data){
		return a.id<b.id;
	}
	return a.data<b.data;
}
int main(){
	int T;
	scanf("%d",&T);
	for(int p=1;p<=T;p++){
		scanf("%d",&n);
		flat.clear();
		memset(b,0,sizeof(b));
		int ma=0,w=0;
		int ans=0;
		for(int i=1;i<=n;i++){
			scanf("%s",s+1);
			int len=strlen(s+1);
			int a=0;
			if(s[1]=='n'){
				flat[w]++;
			}
			else{
				for(int j=1;j<=len;j++){
					a=a*10+s[j]-'0';
				}
//				cout<<a<<endl;
				ans++;
				b[ans].data=a;
				b[ans].id=ans;
				if(ma<=a){
					ma=a;
					w=ans;
				}
			}
		}
		sort(b+1,b+ans+1,amp);
		for(int i=0;i<=ans;i++){
			if(i!=0) cout<<b[i].data<<" ";
			for(int j=1;j<=flat[b[i].id];j++){
				cout<<"nan ";
			}
		}
		cout<<endl;
	}
}

混乱邪恶#

不要随便用 map

这里学习了一下 multiset

点击查看代码

struct asd{
	int id,data,id1,id2;
	friend bool operator <(asd a,asd b){//注意,multiset是具有顺序的,所以你结构体要进行自我排序,否则会报错。
		return a.data<b.data;
	}
}b[N];
multiset < asd > s;//定义一个multiset ,可以为结构体,int,pair等等,Multiset和Set的区别就是可以保存多个相同的对象
multiset< asd >::iterator t1,t2;// 定义迭代器
t1=s.begin();//返回第一个元素的位置
t2=s.end();//返回最后一个元素的后一个位置 注意这是后一个
asd qwq=*t1;//得到第一个位置元素

//s.erase(v):删除值为v的所有元素。
//s.erase(it):删除迭代器it处的元素。
//s.find(v):返回一个等于v的迭代器指针。如果不存在值等于v的元素,则返回s.end()
//s.lower_bound(v):返回第一个大于等于v的迭代器指针。
//s.upper_bound(v):返回第一个大于v的迭代器指针。
//s.count(v):返回值等于v的元素个数,数据类型为unsigned longlong int,如果不存在返回0,时间复杂度未知,如果重复的个数过多可能会慢。
//可以用s.erase(s.find(v))来删除值为v的一个元素

校门外歪脖树上的鸽子#

首先考虑暴力,类似线段树,首先你要先dfs出每个节点子树的左右节点,然后修改查询时要考虑左儿子右边界是否大于查询左边界,右儿子左边界是否小于查询有边界,进行 dfs 46pts

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2*1e5+5;
int tr[N*4][3];
struct asd{
	int l,r,data;
}tree[N*4];
int n,m;
void dfs(int x){
	if(x<=n){
		tree[x].l=tree[x].r=x;
		return;
	}
	dfs(tr[x][0]),dfs(tr[x][1]);
	if(tree[tr[x][0]].l>=tree[tr[x][1]].l){
		swap(tr[x][1],tr[x][0]);
	}
	tree[x].l=tree[tr[x][0]].l;
	tree[x].r=tree[tr[x][1]].r;
}
void change(int p,int l,int r,int d){
	if(tree[p].l>=l && tree[p].r<=r){
		tree[p].data+=(tree[p].r-tree[p].l+1)*d;
		return;
	}
	if(tree[tr[p][0]].r>=l) change(tr[p][0],l,r,d);
	if(tree[tr[p][1]].l<=r) change(tr[p][1],l,r,d);
}
int ask(int p,int l,int r){
	if(tree[p].l>=l && tree[p].r<=r){
		return tree[p].data;
	}
	int ans=0;
	if(tree[tr[p][0]].r>=l) ans+=ask(tr[p][0],l,r);
	if(tree[tr[p][1]].l<=r) ans+=ask(tr[p][1],l,r); 
	return ans;
}
bool flat[N*2];
signed main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<n;i++){
		int x,y;
		scanf("%lld%lld",&x,&y);
		tr[n+i][0]=x,tr[n+i][1]=y;
		flat[x]=flat[y]=1;
	}
	int root;
	for(int i=1;i<=n*2-1;i++){
		if(flat[i]==0){
			root=i;
			break;
		}
	}
	dfs(root);
	for(int i=1;i<=m;i++){
		int op;
		scanf("%lld",&op);
		if(op==1){
			int l,r,d;
			scanf("%lld%lld%lld",&l,&r,&d);
			change(root,l,r,d);
		}
		else{
			int l,r;
			scanf("%lld%lld",&l,&r);
			printf("%lld\n",ask(root,l,r));
		}
	}
}
考虑正解:

定义 isa 表示 a 是否是它父亲的左儿子

定义 ffa 表示 a的真祖先中第一个和 a is 值不同的祖先的兄弟节点

连接 a,ffa ,显然仍能得到一颗以原来根为根的树(以后就说现树)。

如何建:

查询两个点 l,r,求出它们 lca 以此为界限将其一分为二,先只考虑左边。

首先由左节点出发向右上走到头为 u ,则 u 为符合的节点

开个栈,每次先将当前点的左儿子与栈顶元素相连,然后将当前点左儿子压入栈,遍历当前点右子树,再将左儿子弹出栈,再遍历左子树,如此得到。可以在 dfs 时直接开一个变量表示栈顶,省掉栈。

点击查看代码
void Do(int th,int x){
	if(!ch[th][0]) return;
	add(x,ch[th][0]);
	Do(ch[th][1],ch[th][0]);
	Do(ch[th][0],x);
}

void Do1(int th,int x){
	if(!ch[th][0]) return;
	add(x,ch[th][1]);
	Do1(ch[th][0],ch[th][1]);
	Do1(ch[th][1],x);
}

	Do(root,root);
	Do1(root,root);

先对于节点 l ,我们找到 l 在原树上一直向右上跳的最远位置(设其为 u

那么从 u 开始在现树上往上跳,发现一定能跳到 lca 的右儿子,但是右儿子又不会和左边的操作相关,所以跳的时候判停。

考虑三种情况:

1:lru 深度均比 lca 小, 则此时需操作的就是 lca 这个点

2:lu 深度比 lca 小,则左侧操作的应该为 lca 的左儿子节点。

3:ru 深度比 lca 小,方法与 2 相同。

因此可以用树剖+线段树维护。

线段树维护节点状态之和,可以打延迟标记。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=4*1e5+5;
int n,m,root;
int ch[N*2][3];
struct asd{
	int l,r,data;
}t[N*2];
struct qwe{
	int l,r,data,num,lazy;
}tr[1600010];
int ll[N*2],rr[N*2];
int head[N*2],ver[N*2],nex[N*2],tot=0;
int fa[N*2],size[N*2],son[N*2],top[N*2],d[N*2];
int _fa[N*2],_size[N*2],_son[N*2],_top[N*2],_d[N*2],_id[N*2],_rk[N*2],cnt=0;

void add(int x,int y){
	ver[++tot]=y,nex[tot]=head[x],head[x]=tot;
}

void dfs(int x){
	if(x<=n){
		t[x].l=t[x].r=x;
		return;
	}
	dfs(ch[x][0]);
	dfs(ch[x][1]);
	if(t[ch[x][0]].l>=t[ch[x][1]].l){
		swap(ch[x][1],ch[x][0]);
	}
	t[x].l=t[ch[x][0]].l;
	t[x].r=t[ch[x][1]].r;
}

void dfs1(int x){
	if(x<=n) return;
	rr[ch[x][0]]=rr[x],ll[ch[x][0]]=ch[x][0];
	dfs1(ch[x][0]);
	ll[ch[x][1]]=ll[x],rr[ch[x][1]]=ch[x][1];
	dfs1(ch[x][1]);
}

void dfs_y1(int x,int f){
	if(!x) return; 
	d[x]=d[f]+1;
	fa[x]=f;
	size[x]=1;
	dfs_y1(ch[x][0],x); size[x]+=size[ch[x][0]];
	dfs_y1(ch[x][1],x); size[x]+=size[ch[x][1]];
	if(size[ch[x][0]]<size[ch[x][1]]) son[x]=ch[x][1];
	else son[x]=ch[x][0];
}

void dfs_y2(int x,int tp){
	if(!x) return;
	top[x]=tp;
	if(son[x]) dfs_y2(son[x],tp);
	if(ch[x][0]!=son[x]) dfs_y2(ch[x][0],ch[x][0]);
	else dfs_y2(ch[x][1],ch[x][1]);
}

int ask_lca(int x,int y){//原树lca 
	int fx=top[x],fy=top[y];
	while(fx!=fy){
		if(d[fx]>d[fy]) x=fa[fx],fx=top[x];
		else y=fa[fy],fy=top[y];
	}
	if(d[x]<d[y]) return x;
	else return y;
}

void dfs_x1(int x,int f){
	_d[x]=_d[_fa[x]]+1;
	_size[x]=1;
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		_fa[y]=x; 
		dfs_x1(y,x);
		_size[x]+=_size[y];
		if(_size[_son[x]]<_size[y]){
			_son[x]=y;
		}
	}
}

void dfs_x2(int x,int tp){
	_id[x]=++cnt;
	_rk[cnt]=x;
	_top[x]=tp;
	if(_son[x]) dfs_x2(_son[x],tp);
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(y==_fa[x]) continue;
		if(y!=_son[x]){
			dfs_x2(y,y);
		}
	}
}

void Do(int th,int x){
	if(!ch[th][0]) return;
	add(x,ch[th][0]);
	Do(ch[th][1],ch[th][0]);
	Do(ch[th][0],x);
}

void Do1(int th,int x){
	if(!ch[th][0]) return;
	add(x,ch[th][1]);
	Do1(ch[th][0],ch[th][1]);
	Do1(ch[th][1],x);
}

void build(int p,int l,int r){
	tr[p].l=l,tr[p].r=r;
	if(l==r){
		int id=_rk[l];
		tr[p].num=(t[id].r-t[id].l+1);
		return;
	}
	int mid=(l+r)/2;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	tr[p].num=tr[p*2].num+tr[p*2+1].num;
}

void pushdown(int p){
	if(tr[p].lazy){
		tr[p*2].data+=tr[p*2].num*tr[p].lazy;
		tr[p*2].lazy+=tr[p].lazy;
		tr[p*2+1].data+=tr[p*2+1].num*tr[p].lazy;
		tr[p*2+1].lazy+=tr[p].lazy;
		tr[p].lazy=0;
	}
}

void change(int p,int l,int r,int w){
	if(tr[p].l>=l && tr[p].r<=r){
		tr[p].data+=tr[p].num*w;
		tr[p].lazy+=w;
		return;
	}
	pushdown(p);
	int mid=(tr[p].l+tr[p].r)/2;
	if(l<=mid) change(p*2,l,r,w);
	if(r>mid) change(p*2+1,l,r,w);
	tr[p].data=tr[p*2].data+tr[p*2+1].data;
	return;
}

int ask(int p,int l,int r){
	if(tr[p].l>=l && tr[p].r<=r){
		return tr[p].data;
	}
	pushdown(p);
	int mid=(tr[p].l+tr[p].r)/2;
	int ans=0;
	if(l<=mid) ans+=ask(p*2,l,r);
	if(r>mid) ans+=ask(p*2+1,l,r);
	return ans;
}

void chan(int l,int r,int w){
	int lca=ask_lca(l,r),ans=0;
	l=rr[l],r=ll[r];
	if(d[l]<=d[lca] && d[r]<=d[lca]){
		change(1,_id[lca],_id[lca],w);
		return;
	}	
	
	if(d[l]<=d[lca]){
		l=ch[lca][0];
		change(1,_id[l],_id[l],w);
	}
	else{
		int fx=_top[l];
		while(_d[fx]>_d[ch[lca][1]]){
			change(1,_id[fx],_id[l],w);
			l=_fa[fx],fx=_top[l];
		}
		change(1,_id[ch[lca][1]]+1,_id[l],w);
	}
	if(d[r]<=d[lca]){
		r=ch[lca][1];
		change(1,_id[r],_id[r],w);
	}
	else{
		int fx=_top[r];
		while(_d[fx]>_d[ch[lca][0]]){
		     change(1,_id[fx],_id[r],w);
		     r=_fa[fx],fx=_top[r];
		}
		change(1,_id[ch[lca][0]]+1,_id[r],w);
	}
}

int askk(int l,int r){
	int lca=ask_lca(l,r),ans=0;
	l=rr[l],r=ll[r];
	if(d[l]<=d[lca] && d[r]<=d[lca]){
		ans+=ask(1,_id[lca],_id[lca]);
		return ans;
	}	
	
	if(d[l]<=d[lca]){
		l=ch[lca][0];
		ans+=ask(1,_id[l],_id[l]);
	}
	else{
		int fx=_top[l];
		while(_d[fx]>_d[ch[lca][1]]){
			ans+=ask(1,_id[fx],_id[l]);
			l=_fa[fx],fx=_top[l];
		}
		ans+=ask(1,_id[ch[lca][1]]+1,_id[l]);
	}
	if(d[r]<=d[lca]){
		r=ch[lca][1];
		ans+=ask(1,_id[r],_id[r]);
	}
	else{
		int fx=_top[r];
		while(_d[fx]>_d[ch[lca][0]]){
		     ans+=ask(1,_id[fx],_id[r]);
		     r=_fa[fx],fx=_top[r];
		}
		ans+=ask(1,_id[ch[lca][0]]+1,_id[r]);
	}
	return ans;
}

bool flat[N*2];
signed main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<n;i++){
		int x,y;
		scanf("%lld%lld",&x,&y);
		ch[n+i][0]=x,ch[n+i][1]=y;
		flat[x]=flat[y]=1;
	}
	for(int i=1;i<=n*2-1;i++){
		if(flat[i]==0){
			root=i;
			break;
		}
	}
	dfs(root);
	ll[root]=root,rr[root]=root;
	dfs1(root);
	Do(root,root);
	Do1(root,root);
	dfs_y1(root,0);
	dfs_y2(root,root);
	dfs_x1(root,0);
	dfs_x2(root,root);
	build(1,1,cnt);
	for(int i=1;i<=m;i++){
		int op;
		scanf("%lld",&op);
		if(op==1){
			int l,r,d;
			scanf("%lld%lld%lld",&l,&r,&d);
			chan(l,r,d);
		}
		else{
			int l,r;
			scanf("%lld%lld",&l,&r);
			printf("%lld\n",askk(l,r));
		}
	}
}

csp模拟4#

最长上升子序列#

题目大意为给出一个长度为 kk 的最长上升序列,求 11 到 nn 合法排列中字典序最小的一个。

思考当给出的 k1 时,直接将 1n 倒序输出即可,进行特判;

不妨令 A 的补集为 B,在每个 AiB 找到未填入的小于 Ai 的最小的数,处理到 Ak,剩下的数倒序输出就可以了,注意特判 Ak,在前面输出 (n Ak+1);

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2*1e5+10;
int a[N];
int b[N];
bool ask[N];
int tot=1;
int main(){
	int n,k;
	scanf("%d%d",&n,&k);
	if(k==1){
		for(int i=n;i>=1;i--){
			cout<<i<<" ";
		}
		return 0;
	}
	for(int i=1;i<=k;i++){
		scanf("%d",&a[i]);
		ask[a[i]]=1;
	}
	tot=1;
	while(tot<=n && ask[tot]){
		tot++;
	}
	for(int i=1;i<=k;i++){
		if(i==k){
			for(int j=n;j>a[i];j--){
				ask[j]=1;
				printf("%d ",j);
			}
		}
		printf("%d ",a[i]);
		if(tot<a[i] && i!=k){
			ask[tot]=1;
			cout<<tot<<" ";
			tot++;
			while(tot<=n && ask[tot]){
	         	tot++;
          	}
		}
	}
	for(int i=n;i>=1;i--){
		if(ask[i]==0){
			cout<<i<<" ";
		}
	}
}

独特序列#

定义 f[i] 表示以 i 结尾的独特序列。

pre[i] 表示上一次出现 数a[i] 的位置。

考虑转移:

f[i]=j=pre[a[i]]i1f[j][j!=prek(1<=k<=i1)]

在考虑优化,用树状数组,每次算完 f[i] 将此插到 if[pre[i]] 删去。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
const int N=2*1e5+10;
int a[N];
int c[N];
int f[N];
int pre[N];
int flat[N];
int n;
int lowbit(int x){
	return x&(-x);
}
void add(int x,int d){
	if(x==0) return;
	for(;x<=N;x+=lowbit(x)){
		c[x]=(c[x]+d+mod)%mod;
	}
}
int ask(int x){
	if(x==0) return 0;
	int ans=0;
	for(;x;x-=lowbit(x)){
		ans=(ans+c[x])%mod;
	}
	return ans;
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		pre[i]=flat[a[i]];
		flat[a[i]]=i;
	}
	for(int i=1;i<=n;i++){
		if(pre[i]==0){
			f[i]=(ask(i-1)+1)%mod;
		}
		else{
			f[i]=(ask(i-1)-ask(pre[i]-1)+mod)%mod;
	    	add(pre[i],-f[pre[i]]);
		}
		add(i,f[i]);
	}
	cout<<ask(n)<<endl;
}

最大GCD#

假二分可得65。

在卡一下时间,特判特解,就可A掉。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3*1e5+10;
int mi=(1ll<<32);
int a[N];
int n,k;
int check(int t){
	int ans=0;
	for(int i=1;i<=n;i++){
		if(a[i]%t==0) continue;
		if(a[i]<t){
			ans+=(t-a[i]);
		}
		else{
			ans+=(t-a[i]%t);
		}
	}
	if(ans<=k) return 1;
	else return 0;
}
signed main(){
	double st=clock();
	scanf("%lld%lld",&n,&k);
	int ll;
	int sum=0;
	int ma=0;
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		sum+=a[i];
		ma=max(a[i],ma);
		mi=min(a[i],mi);
		if(i==1) ll=a[i];
		else ll=__gcd(ll,a[i]);
	}
	if(sum+k>=ma*n){
		cout<<ma+((sum+k)-ma*n)/n<<endl;
		return 0; 
	}
	int l=1,r=mi+k;
	while(l<r){
		int mid=(l+r+1)/2;
		if(check(mid)) l=mid;
		else{
			r=mid-1;
		}
	}
	int w=l;
	double ed;
	for(int i=l+1;i<=mi+k;i++){
		if(check(i)){
			w=i;
		}
		ed=clock();
		if((ed-st)/CLOCKS_PER_SEC>=0.790) {
			cout<<w;
			return 0;
		}
	}
}

正解: num[i]i 小的数的个数

sum 所给序列总和

ma 序列最大值

if(sum+k>=ma*n){ cout<<ma+((sum+k)-ma*n)/n<<endl; return 0; }

思考普遍情况,最大边界为 i=ma ,然后循环判断是否可行,可行直接输出。

如何判断: 将序列补成 i 的倍数,计算出所需,然后判断是否可以。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3*1e5+10;
int mi=(1ll<<32);
int a[N];
int n,k;
int num[N],num1[N];
signed main(){
	double st=clock();
	scanf("%lld%lld",&n,&k);
	int ll;
	int sum=0;
	int ma=0;
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		num[a[i]]++;
		sum+=a[i];
		ma=max(a[i],ma);
	}
	if(sum+k>=ma*n){
		cout<<ma+((sum+k)-ma*n)/n<<endl;
		return 0; 
	}
	for(int i=1;i<=ma;i++){
		num[i]=num[i-1]+num[i];
	}
	for(int i=ma;i>=1;i--){
		int cnt=0;
		for(int j=1;j*i<=ma*2 && i*(j-1)<=ma;j++){
			cnt+=(num[min(ma,i*j)]-num[i*(j-1)])*i*j;
		}
		int xq=cnt-sum;
		if(xq<=k){
			cout<<i<<" ";
			return 0;
		}
	}
}

连续子段#

f[i][s] 表示到第i位,连续子段状态为 s

思考两种点:

关键点:最终为连续子段,贡献为当前点对 s 的逆序对

非关键点:最终不为连续子段、贡献为 min(popcount(s),kpopcount(s))

转移时,每个点都可以为非,但是并不是都可以为关键点

可以滚掉一维,但循环顺序要改变,避免更新重复。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int a[202];
int dp[65540];
int num[65540];
signed main(){
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    int t=(1<<k);
    for(int i=0;i<=t;i++){
    	num[i]=__builtin_popcount(i);
	}
	memset(dp,0x7f,sizeof(dp));
	dp[0]=0;
    int ma=(1<<30);
    for(int i=1;i<=n;i++){
        for(int s=t-1;s>=0;s--){
        	int cnt=__builtin_popcount(s);
        	if(!(s&(1<<(a[i]-1)))){
        		dp[s|(1<<(a[i]-1))]=min(dp[s|(1<<(a[i]-1))],dp[s]+num[s>>a[i]<<a[i]]);
			} 
			dp[s]+=min(cnt,k-cnt);
        }
    }
    cout<<dp[t-1];
}

csp模拟8#

Coprime 2#

考场上想到一个60分的暴力,就是把所有的 A 质因数分解,再分解 &1~m& 看是否有冲突。

O(nlog(log(n)))做法:可以进行埃氏筛,筛的过程中看是否有 A 中出现的数,若有则将整个序列打标记,判断当前数是否可以为答案。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2000005;
int a[N],b[N],c[N];
int n,m;
int ans[N];
bool ask[N+10],flat[N+10];
void getphi(){
	ans[++ans[0]]=1;
	for(int i=2;i<=m;i++){
		int op=0;
		if(!flat[i]){
			for(int j=2;i*j<=m;j++){
				flat[i*j]=1;
			 	if(b[i*j]) op=1;
			}
			if(op){
				for(int j=2;i*j<=m;j++){
					c[i*j]=1;
				}
			}
		}
		if(!b[i] && !op && !c[i]) ans[++ans[0]]=i;
	}
}
signed main(){
	memset(b,0,sizeof(b));
	memset(ask,0,sizeof(ask));
	memset(flat,0,sizeof(flat));
	memset(c,0,sizeof(c));
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		b[a[i]]=1;
	}
	getphi();
	cout<<ans[0]<<endl;
	for(int i=1;i<=ans[0];i++){
		printf("%lld\n",ans[i]);
	}
}

Dist Max 2#

考场上只写出一个 nn 的暴力,得到 20pts.

一眼就可以看出是二分,但是如何查找,是个问题。

这就要引用学长的话:"遇事不决就排序"。

所以按照左端点进行排序,维护一个双指针 i,j,使得 ajai>d 时,分为三部分,前缀,中间,和后缀,这是只要前缀最大-后缀最小或者后缀最小-前缀最大,就可以符合当前 checkd.

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int M=1e9+10;
struct asd{
	int x,y;
}a[N];
int n;
int maq[N],miq[N],mah[N],mih[N];
bool amp(asd a,asd b){
	if(a.x==b.x) return a.y<b.y;
	else return a.x<b.x;
}
int check(int d){
	for(int i=1,j=1;i<=n;i++){
		while(j<n && (a[j].x-a[i].x)<d) j++;
		if((a[j].x-a[i].x)>=d){
			if((mah[j]-miq[i])>=d || (maq[i]-mih[j])>=d) return 1;
		}
	}
	return 0;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&a[i].x,&a[i].y);
	}
	sort(a+1,a+n+1,amp);
	maq[1]=a[1].y;
	miq[1]=a[1].y;
	for(int i=2;i<=n;i++){
		maq[i]=max(maq[i-1],a[i].y);
		miq[i]=min(miq[i-1],a[i].y);
	}
	mah[n]=a[n].y;
	mih[n]=a[n].y;
	for(int i=n-1;i>=1;i--){
		mah[i]=max(mah[i+1],a[i].y);
		mih[i]=min(mih[i+1],a[i].y);
	}
	int l=0,r=M;
	while(l<r){
		int mid=(l+r+1)/2;
		if(check(mid)) l=mid;
		else r=mid-1;
	}
	cout<<l<<endl;
}

Count Multiset#

考场上不会。

dpdpi,j 表示填了 i 个数,总和为 j,含零;

有一种很神奇的转移,就是加一加零,我感觉就是方便转移,或者是因为可以看成单调递增序列,所以进行排序后是一样的。

但是每个数有限制,所以零不能无限制的加,所以设 gi,j 表示填了 i 个数,总和为 j,且不含零。

转移就出来了:
gi,j=fi,ji 相当与给 f 序列加一

fi,j=fi,ji+k=1mgik,j 相当于有两个转移,加一和加零

可以进行前缀和优化,复杂度 n2

暴力代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
int f[5005][5005];
int g[5005][5005];
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		if(i<=m) f[i][0]=1;
		for(int j=1;j<=n;j++){
			int ans=0;
			for(int k=1;k<=m;k++){
				if(i>=k)
				ans=(ans+g[i-k][j])%mod;
			}
			f[i][j]=(f[i][j]+ans)%mod;
			if(j>=i)
			f[i][j]=(f[i][j]+f[i][j-i])%mod;
			if(j>=i)
			g[i][j]=f[i][j-i];
		}
	}
	for(int i=1;i<=n;i++){
		cout<<g[i][n]<<endl;
	}
}
正解
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
int f[5005][5005];
int g[5005][5005];
int sum[5005][5005];
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		if(i<=m) f[i][0]=1;
		for(int j=1;j<=n;j++){
			int ans=0;
			ans=(sum[i-1][j]-sum[i-m-1][j]+mod)%mod;
			f[i][j]=(f[i][j]+ans)%mod;
			if(j>=i) f[i][j]=(f[i][j]+f[i][j-i])%mod;
			if(j>=i) sum[i][j]=(sum[i][j]+f[i][j-i])%mod;
			sum[i][j]=(sum[i][j]+sum[i-1][j])%mod;
		}
	}
	for(int i=1;i<=n;i++){
		cout<<f[i][n-i]<<endl;
	}
}

Julia the snail#

考试是打的 n2 暴力得了 40pts
吉司机线段树:

吉司机线段树上的叶子结点 i 存储当询问的 a=i 时的答案。

考虑将所有的询问按照右端点从小到大排序。我们维护一个指针 p,遇到一个新的询问,就将 p 向右移动到新的询问的右端点。并做以下处理:

对于指针移动过程中遇到的所有 r,那么对于线段树下标区间为 [1,l] 中的、所有大于 l 的数(表明可以被更新),将它的值变为 r

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1000005;
const int inf=1e7;
struct asd{
	int l,r,ans,id;
}b[N],c[N];
struct qwe{
	int l,r,fir,sec,lazy1,lazy2;
}tr[N*4];
int pre[N];
int ans[N];
int n,m;
bool amp(asd a,asd b){
	return a.r<b.r;
}
void pushup(int p){//合并左右子树 
	if(tr[p*2].fir==tr[p*2+1].fir){
		tr[p].fir=tr[p*2].fir;
		tr[p].sec=max(tr[p*2].sec,tr[p*2+1].sec);
	}
	else if(tr[p*2].fir<tr[p*2+1].fir){
		tr[p].fir=tr[p*2+1].fir;
		tr[p].sec=max(tr[p*2].fir,tr[p*2+1].sec);
	}
	else{
		tr[p].fir=tr[p*2].fir;
		tr[p].sec=max(tr[p*2].sec,tr[p*2+1].fir);
	}
}
void push(int p,int s1,int s2){//更改延迟标记 ,这里的情况还要考虑普通的延迟标记下放 
	if(tr[p].fir<s1) return;
	tr[p].fir=s2;
	tr[p].lazy1=min(tr[p].lazy1,s1);
	tr[p].lazy2=s2;
}
void pushdown(int p){//下传延迟标记 
	if(tr[p].lazy1!=inf){
		push(p*2,tr[p].lazy1,tr[p].lazy2);
		push(p*2+1,tr[p].lazy1,tr[p].lazy2);
		tr[p].lazy1=tr[p].lazy2=inf;
	}
}
void build(int p,int l,int r){
	tr[p].l=l,tr[p].r=r;
	tr[p].lazy1=tr[p].lazy2=inf;
	if(l==r){
		tr[p].fir=l;
		tr[p].sec=-1;
		return;
	}
	int mid=(l+r)/2;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	pushup(p);
}
void change(int p,int l,int r,int s1,int s2){
	if(tr[p].fir<s1) return;//当前节点不符合更新条件 
	if(tr[p].l>=l && tr[p].r<=r && s1>tr[p].sec){//要大于次大值,进行不断递归 
		push(p,s1,s2);
		return;
	}
	pushdown(p);
	int mid=(tr[p].l+tr[p].r)/2;
	if(l<=mid) change(p*2,l,r,s1,s2);
	if(r>mid) change(p*2+1,l,r,s1,s2);
	pushup(p);
}
int ask(int p,int wh){
	if(tr[p].l==tr[p].r) return tr[p].fir;
	pushdown(p);
	int mid=(tr[p].l+tr[p].r)/2;
	if(wh<=mid) return ask(p*2,wh);
	return ask(p*2+1,wh);
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d%d",&b[i].l,&b[i].r);
	}
	int q;
	scanf("%d",&q);
	for(int i=1;i<=q;i++){
		scanf("%d%d",&c[i].l,&c[i].r);
		c[i].id=i;
	}
	sort(c+1,c+q+1,amp);
	sort(b+1,b+m+1,amp);
	build(1,1,n);
    int op=1;
	for(int i=1;i<=q;i++){
        while(b[op].r<=c[i].r && op<=m){
        	change(1,1,b[op].l,b[op].l,b[op].r);op++;
		}
		ans[c[i].id]=ask(1,c[i].l);
	}
	for(int i=1;i<=q;i++){
		printf("%d\n",ans[i]);
	}
}
/*
8
4
1 2
3 4
2 5
6 7
5
1 2
1 4
1 6
2 7
6 8
*/

csp模拟6#

排序#

首先找到所有的逆序对,前提是所有的逆序对都要用到,在考虑从大往小替换,思考 n ,此时有众多逆序对,最初可以判断的是最后一次交换是把 n 交换到此处,设一开始在 n 的数为 x,所以需要交换的有 x+1 n 交换过后你不可以破坏 x+1 n1 的逆序对,所以是从小到大依次交换,这样区间就变成 n1.

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int a[1005];
int pos[1005];
struct asd{
	int x,y;
}b[1005];
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		pos[a[i]]=i;
	}
	int cnt=0;
	for(int i=2;i<=n;i++){
		for(int j=i-1;j>=1;j--){
			if(a[j]>a[i]){
				cnt++;
			}
		}
	}
	cout<<cnt<<endl;
    for(int i=n;i>=1;i--){
    	for(int j=a[i]+1;j<=i;j++){
    		cout<<pos[j]<<" "<<i<<endl;
    		swap(a[pos[j]],a[i]);
    		pos[j-1]=pos[j];
    		pos[j]=i;
		}
	}
}
/*
10
2 6 8 5 3 7 4 10 9 1 
*/

无聊的卡牌问题#

我们可以首先处理出来哪几张牌为一组,可以通过一个栈处理出来,然后再跑一遍栈,处理出来先后关系,具体操作就是从 1 压到 n ,若栈顶三个元素属于统一方,则栈顶一组为删除栈顶三个元素之后栈顶所属组的充分条件。

之后就跑拓扑,考虑当先所需的为先手方还是后手方,用异或转移,还有要有叶子节点先取叶子节点(注意独立点不是叶子节点),没有的情况下再取独立店,而且叶子节点数量要区分先后手。

可以根据这张图分析一下:
lhx 是神

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2000;
int a[N],b[N];
int flat[N];
int sy[N],rk[N];
int head[N*2],ver[N*2],nex[N*2],tot=0;
struct asd{
	int x,y,z;
}tr[N];
void add(int x,int y){
	ver[++tot]=y;nex[tot]=head[x];head[x]=tot;
}
int s[N];
int rd[N],cd[N];
queue<int>q;
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n*3;i++){
		scanf("%d",&a[i]);
		flat[a[i]]=1;
	}
	int fk=0;
    int sum=0;
	int top=0;
	for(int i=1;i<=n*6;i++){
		s[++top]=i;
		if(top>=3){
			if(top && flat[s[top]]==flat[s[top-1]] && flat[s[top]]==flat[s[top-2]]){
				tr[++sum]={s[top-2],s[top-1],s[top]};
				sy[s[top-2]]=sy[s[top-1]]=sy[s[top]]=sum;
				rk[sum]=flat[s[top]];
				top-=3;
			}
		}
	}
	for(int i=1;i<=n*6;i++){
		s[++top]=i;
		if(top>=3){
			if(top && flat[s[top]]==flat[s[top-1]] && flat[s[top-1]]==flat[s[top-2]]){
				if(top-3>0){
					add(sy[s[top]],sy[s[top-3]]);
					rd[sy[s[top-3]]]++;
					cd[sy[s[top]]]++;
				}
				top-=3;
			}
		}
	}
	bool nm[N];
	int left[2]={0,0};
	for(int i=1;i<=sum;i++){
		nm[i]=0;
		if(!rd[i]){
			if(cd[i]){
				left[rk[i]]++;
				nm[i]=1;
			}
			q.push(i);
		}
	}
	int w=1;
	while(!q.empty()){
		int x=q.front();
		q.pop();
		while(rk[x]!=w ||( left[w] && !nm[x])){
			q.push(x);
			x=q.front();
			q.pop();
		}
		for(int i=head[x];i;i=nex[i]){
			int y=ver[i];
			rd[y]--;
			if(rd[y]==0){
				if(cd[y]){
					left[rk[y]]++;
					nm[y]=1;
				}
				q.push(y); 
			}
		}
		if(nm[x]) left[rk[x]]--;
		printf("%d %d %d\n",tr[x].x,tr[x].y,tr[x].z);
		w^=1;
	}
}

csp模拟9#

最短路#

考虑给 a 排序,之后跑 floyed ,转移的时候最短路的最大值已知,顺便更新答案就可以了。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=305;
int n,m,Q;
struct asd{
	int d,id;
}a[N];
int f[N][N];
int g[N][N],ma[N][N];
int mp[N];
int amp(asd a,asd b){
	return a.d<b.d;
}
signed main(){
	freopen("path.in","r",stdin);
	freopen("path.out","w",stdout);
	scanf("%lld%lld%lld",&n,&m,&Q);
	memset(f,0x3f,sizeof(f));
	memset(g,0x3f,sizeof(g)); 
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i].d);
		a[i].id=i;
	}
    sort(a+1,a+n+1,amp);
    for(int i=1;i<=n;i++){
    	mp[a[i].id]=i;
	}
	for(int i=1;i<=m;i++){
		int x,y,z;
		scanf("%lld%lld%lld",&x,&y,&z);
		f[mp[x]][mp[y]]=f[mp[y]][mp[x]]=min(f[mp[x]][mp[y]],z);
	}
	
	for(int i=0;i<=n;i++){
		f[i][i]=0;
	}
    for(int k=1;k<=n;k++){
	    for(int i=1;i<=n;i++){
 		   	for(int j=1;j<=n;j++){
    			f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
                g[i][j]=min(g[i][j],f[i][j]+max(a[i].d,max(a[j].d,a[k].d)));
			}
		}
	}
	for(int i=1;i<=Q;i++){
		int x,y;
		scanf("%lld%lld",&x,&y);
		if(g[mp[x]][mp[y]]>1e12){
			cout<<"-1"<<endl;
		}
		else{
	    	cout<<g[mp[x]][mp[y]]<<endl;
		}
	}
}
/*

4 5 3
2 4 4 5 
2 3 3
4 1 5
4 2 5
1 2 1
3 2 2
3 2 
2 1 
4 3 

6
5
12
*/

方格取数#

考场上想到的是二分套二分,得到 75pts,首先确定一个左下角的点,在以此点开始二分另一个点的横坐标,确定横坐标后在二分纵坐标,总复杂度 O(n2log2n) 在加一些特判减少循环是可以卡过的。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2005;
int n,k;
int a[N][N];
int sum[N][N];
int ask(int x,int y,int x1,int y1){
	int ans=sum[x1][y1]-sum[x1][y-1]-sum[x-1][y1]+sum[x-1][y-1];
	return ans;
}
int check(int x,int y,int x1){
	if(ask(x,y,x1,y)>2*k){
		return 0;
	}
	int l=y-1,r=n+1;
	while(l<r){
		int mid=(l+r+1)/2;
		int cnt=ask(x,y,x1,mid);
		if(cnt<k) l=mid;
		else if(cnt>k*2) r=mid-1;
		else{
			printf("%lld %lld %lld %lld",x,y,x1,mid);
			exit(0);
		}
	}
	if(l<=y) return 0;
	else return 1;
}
signed main(){
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);
	scanf("%lld%lld",&n,&k);
	int opt=0;
	int x1,x2,y1,y2,fk=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			scanf("%lld",&a[i][j]);
			if(a[i][j]>=k && a[i][j]<=k*2){
				fk=1;
				x1=i,x2=i,y1=j,y2=j;
			}
			if(a[i][j]<k) opt=1; 
		}
	}
	if(fk==1){
		cout<<x1<<" "<<y1<<" "<<x2<<" "<<y2<<endl;
		return 0;
	}
	if(opt==0){
		cout<<"-1"<<endl;
		return 0;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+a[i][j];
		}
	}
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=n;j++){
    		if(ask(i,j,i,j)>k*2){
    			continue;
			}
    		int l=i-1,r=n+1;
    		while(l<r){
    			int mid=(l+r+1)/2;
    			if(check(i,j,mid)) l=mid;
    			else r=mid-1;
			}
		}
	}
	cout<<"-1"<<endl;
}



/*
3 5
6 15 5
11 6 6
11 6 6

3 5
4 15 3
11 3 4
11 1 1

2 2 3 3
*/

数组#

真的很巧妙,因为 ai<=300 ,所以质数一共有62个,所以可以用一个 longlong 进行状压。

剩下的就是维护一个线段树,维护区间的乘积以及区间所包含的质数。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
const int mod=1e9+7;
int a[N],w[N];
int inv[N];
int prime[100];
bool nprime[N];
int mp[N];
struct asd{
	int l,r,val,lazy1,zt,lazy2;
	asd(){
		val=1ll,lazy1=1ll,lazy2=0,zt=0;
	}
}tr[N*4];
inline int read(){
	int x(0);bool f(0);char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar()) f^=ch=='-';
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
	return f?x=-x:x;
}
int mgml(int x,int p){
	int ans=1;
	while(p){
		if(p&1) ans=(ans*x)%mod;
		x=(x*x)%mod;
		p>>=1;
	}
	return ans;
} 
void work(){
	for(int i=2;i<=300;++i){
		if(!nprime[i]){
			prime[++prime[0]]=i;
		}
		for(int j=1;j<=prime[0] && i*prime[j]<=300;++j){
			int k=i*prime[j];
			nprime[k]=1;
			if(i%prime[j]==0) break;
		}
	}
}

void build(int p,int l,int r){
	tr[p].l=l,tr[p].r=r;
	if(l==r){
		tr[p].val=a[l];
		tr[p].zt=w[l];
		return;
	}
	int mid=(l+r)/2;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	tr[p].val=tr[p*2].val*tr[p*2+1].val%mod;
	tr[p].zt=tr[p*2].zt|tr[p*2+1].zt; 
}
void pushdown(int p){
	if(tr[p].lazy1 || tr[p].lazy2){
		tr[p*2].lazy1=tr[p*2].lazy1*tr[p].lazy1%mod;
		tr[p*2].val=tr[p*2].val*mgml(tr[p].lazy1,tr[p*2].r-tr[p*2].l+1)%mod;
		tr[p*2+1].lazy1=tr[p*2+1].lazy1*tr[p].lazy1%mod;
		tr[p*2+1].val=tr[p*2+1].val*mgml(tr[p].lazy1,tr[p*2+1].r-tr[p*2+1].l+1)%mod;
		tr[p].lazy1=1;
		tr[p*2].lazy2|=tr[p].lazy2;
		tr[p*2].zt|=tr[p*2].lazy2;
		tr[p*2+1].lazy2|=tr[p].lazy2;
		tr[p*2+1].zt|=tr[p*2+1].lazy2;
		tr[p].lazy2=0; 
	}
}
asd ask(int p,int l,int r){
	if(tr[p].l>=l && tr[p].r<=r){
		return tr[p];
	}
	pushdown(p);
	int mid=(tr[p].l+tr[p].r)/2;
	asd x,y,z;
	x.val=y.val=1;
	if(l<=mid) x=ask(p*2,l,r);
	if(r>mid) y=ask(p*2+1,l,r);
	z.val=x.val*y.val%mod;
	z.zt=x.zt|y.zt;
	return z;
}
void change(int p,int l,int r,int v,int z){
	if(tr[p].l>=l && tr[p].r<=r){
		tr[p].lazy1=(tr[p].lazy1*v)%mod;
		tr[p].val=tr[p].val*mgml(v,tr[p].r-tr[p].l+1)%mod;
		tr[p].lazy2|=z;
		tr[p].zt|=z;
		return;
	}
	pushdown(p);
	int mid=(tr[p].l+tr[p].r)/2;
	if(l<=mid) change(p*2,l,r,v,z);
	if(r>mid) change(p*2+1,l,r,v,z);
	tr[p].val=tr[p*2].val*tr[p*2+1].val%mod;
	tr[p].zt=tr[p*2].zt|tr[p*2+1].zt;
}
signed main(){
	freopen("array.in","r",stdin);
	freopen("array.out","w",stdout);
	int n,q;
	work();
	for(int i=1;i<=prime[0];++i){
		mp[prime[i]]=i;
		inv[prime[i]]=mgml(prime[i],mod-2);
	}
	n=read(),q=read();
//	scanf("%lld%lld",&n,&q);
	for(int i=1;i<=n;++i){
		a[i]=read();
//		scanf("%lld",&a[i]);
		int tmp=a[i];
		for(int j=2;j<=tmp;j++){
			if(tmp%j==0){
				w[i]|=(1ll<<(mp[j]-1ll));
				while(tmp%j==0){
					tmp/=j;
				}
			}
		}
	}
	build(1,1,n);
	for(int i=1;i<=q;i++){
		int op;
		op=read();
		if(op==2){
			int x,y;
			x=read(),y=read();
			asd s;
			s=ask(1,x,y);
			int ans1=s.val,ans2=s.zt;
			for(int j=1;j<=62;j++){
				if(ans2&(1ll<<(j-1ll))){
					ans1=ans1*(prime[j]-1)%mod*inv[prime[j]]%mod;
				}
			}
			cout<<ans1<<endl;
		}
		else{
			int x,y,z;
			x=read(),y=read(),z=read();
			int tmp=z,fk=0;
			for(int i=2;i<=tmp;i++){
				if(tmp%i==0){
					fk|=(1ll<<(mp[i]-1ll));
					while(tmp%i==0){
						tmp/=i;
					}
				}
			}
			change(1,x,y,z,fk);
		}
	}
}
/*
4 4
2 4 6 7
2 1 4
1 2 3 3
1 3 4 4
2 3 4

2 12 72 28

96
576
*/

#

考场上打的暴力和部分分,可以拿 60pts .这题再一次用到了 根号分治 ,对于每次跳的长度大于 n ,步数就不超过 n,于是暴跳. 如果步长不大于 n ,那么步长只有 n 种,那么可以进行预处理,设 tpi,j 表示从 i 每次跳 j 步,跳到根的总和,可以 dfs 求出,则可以差分求出两点之间的值。预处理复杂度 O(nnlogn)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=50005;
int tp[N][240]; 
int a[N],b[N],c[N];
int head[N*2],nex[N*2],ver[N*2],fa[N],tot=0;
int f[N][40];
int d[N];
int n;
void add(int x,int y){
	ver[++tot]=y,nex[tot]=head[x],head[x]=tot;
}
void dfs(int x,int far){
	fa[x]=far;
	d[x]=d[far]+1;
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(y==far) continue;
		f[y][0]=x;
		dfs(y,x);
	}
}
void init(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=30;j++){
			f[i][j]=f[f[i][j-1]][j-1];
		}
	}
}
int ask_lca(int x,int y){
	if(d[x]<d[y]) swap(x,y);
	for(int i=16;i>=0;i--){
		if(d[f[x][i]]>=d[y]) x=f[x][i];
	}
	if(x==y) return x;
	for(int i=16;i>=0;i--){
		if(f[x][i]!=f[y][i]){
			x=f[x][i],y=f[y][i];
		}
	}
	return f[x][0];
}
int geten(int x,int k){
	for(int i=16;i>=0;i--){
		if(k>=(1<<i)){
			k-=(1<<i);
			x=f[x][i];
		}
	}
	return x;
}
int get(int x,int y,int fk){
	int lca=ask_lca(x,y);
	int ans=0;
	while(d[x]>d[lca]){
		ans+=a[x];
		x=geten(x,fk);
	}
	while(d[y]>=d[lca]){
		ans+=a[y];
		y=geten(y,fk);
	}
	return ans;
}
void dfs1(int x,int t){
	for(int i=1;i<=t;i++){
		tp[x][i]=tp[geten(x,i)][i]+a[x];
	}
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(y==fa[x]) continue;
		dfs1(y,t); 
	}
}
int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	scanf("%d",&n);
	int t=sqrt(n); 
	int op=0;
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	int op1=0;
	for(int i=1;i<n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);add(y,x);
	}
	dfs(1,0);
	init();
	for(int i=1;i<=n;i++){
		scanf("%d",&b[i]);
	}
	for(int i=1;i<n;i++){
		scanf("%d",&c[i]);
	}
	dfs1(1,t);
//	for(int i=1;i<=n;i++){
//		cout<<tp[i][1]<<endl;
//	}
	for(int i=1;i<n;i++){
		int x=b[i],y=b[i+1];
		if(c[i]>=t){
			printf("%d\n",get(x,y,c[i]));
		}
		else{
			int k=c[i];
			int lca=ask_lca(x,y);
			int ans=tp[x][k]+tp[y][k];
			int tmp=lca;
			while((d[x]-d[tmp])%k!=0 && tmp){
				tmp=fa[tmp];
			}
			ans-=tp[tmp][k];
			tmp=lca;
			while((d[y]-d[tmp])%k!=0 && tmp){
				tmp=fa[tmp];
			}
			ans-=tp[tmp][k];
			if(tmp==lca) ans+=a[lca];
			printf("%d\n",ans);
		}
	} 
}




/*
8
1 4 5 5 2 1 4 5 
1 2
2 3
1 4
4 5
1 6
5 7
5 8
5 2 7 3 1 6 8 4 
3 4 1 1 1 4 2
 

6
8
21
10
2
6
10


*/ 

csp模拟10#

U#

考场上想的是暴力,就是 Cn2k2f/Cnk ,暴力应该可以拿 60 ,所以问题来了,f 怎么求,考虑每条边的贡献次数。设 1 为树的根,对于一条边 (u,v),边权为 cx,y 为经过该边的一条路径,假设 xu 的子树中,那么 y 只能在除 u 的子树 中,求出 x,y 的个数再一相乘就可以.

对于 x 的位置的数量,可以直接在树上从下往上求出。

对于 y 可放置的位置有两种情况:第一种情况:v 到根结点有权值为 c
边,对于这种情况可以通过存权值为 c 的最近父节点。第二种情况:v 到根结点没
有权值为 c 的边,整棵树的大小减去从根往下的第一个权值为 c 的边的子树即可。

top[x] 表示从 x 节点往上找到最近的父亲节点与 x 节点值相等的点,

sum[x] 表示从 x 节点开始向下可以掌控的范围,可以在 dfs 过程中让 sum[top[x]]sum[x] 求出。

之后求 y 的个数,如果 ytop ,则 y 的范围为 sum[y] ,否则为整棵树大小减去所有相同值节点 xsum[x].

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
const int N=2*1e5+10;
int mgml(int x,int p){
	int ans=1;
	while(p){
		if(p&1) ans=(ans*x)%mod;
		x=(x*x)%mod;
		p>>=1;
	}
	return ans;
}
int f[N],inv[N];
void init(){
	f[0]=1;
	inv[0]=1;
	for(int i=1;i<=N;i++){
		f[i]=f[i-1]*i%mod;
		inv[i]=mgml(f[i],mod-2);
	}
}
int c(int x,int y){
	return f[x]*inv[x-y]%mod*inv[y]%mod;
}
int ans[N];
int e[N];
int head[N*2],ver[N*2],nex[N*2],edge[N*2],tot=0;
void add(int x,int y,int v){
	ver[++tot]=y,nex[tot]=head[x],head[x]=tot,edge[tot]=v;
	ver[++tot]=x,nex[tot]=head[y],head[y]=tot,edge[tot]=v;
}
int size[N],fa[N];
int far[N];
int top[N];
int sum[N],cnt[N],zong[N];
void dfs(int x){
	size[x]=1;
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(y==fa[x]) continue;
		e[y]=edge[i];
		fa[y]=x;
		dfs(y);
		size[x]+=size[y]; 
	}
	sum[x]=size[x];
}
void dfs1(int x){
	top[x]=far[e[x]];
	far[e[x]]=x;
	sum[top[x]]-=sum[x];
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(y==fa[x]) continue;
		dfs1(y);
	}
	far[e[x]]=top[x];
}
int data=0;
signed main(){
	int n;
	scanf("%lld",&n);
	int op1=0;
	for(int i=1;i<n;i++){
		int x,y,z;
		scanf("%lld%lld%lld",&x,&y,&z);
		add(x,y,z);
	}
	init();
	dfs(1);
	dfs1(1);
	for(int i=1;i<=n;i++){
		zong[e[i]]+=sum[i];
	}
	for(int i=1;i<=n;i++){
		if(top[i]){
			cnt[i]=sum[top[i]];
		}
		else{
			cnt[i]=size[1]-zong[e[i]];
		}
	}
	for(int i=1;i<=n;i++){
		data+=sum[i]*cnt[i]%mod;
		data%=mod;
	}
	for(int k=2;k<=n;k++){
		ans[k]=c(n-2,k-2)*data%mod*mgml(c(n,k),mod-2)%mod;
	}
	int lhxshishen=0;
	for(int i=1;i<=n;i++){
		lhxshishen^=ans[i];
	}
	cout<<lhxshishen<<endl;
	return 0;
    
}
/*
5
1 2 1
1 3 2
2 4 2
4 5 1

2
1 2 1

3
1 2 1
1 3 2
*/

E#

暴力+线段树

每个点开一个 vector 存全部的区间,然后建树,更改,查询,关键是进行合并和去重,直接暴力就可以。
以两个区间为例,ll,r1,l2,r2 进行合并形成 (ll,r1),(l2,r2),(l1+l2,r1+r2),之后去重。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
struct asd{
	int l,r;
}a[N];
struct qwe{
	int l,r;
}tr[N*4];
struct zxc{
	int l,r;
};
vector<zxc> s[N*4];
zxc b[N*4];
zxc dl;
bool amp(zxc a,zxc b){
	if(a.l==b.l) return a.r<b.r;
	return a.l<b.l;
}
vector<zxc> qc(vector<zxc> k){
	int len=k.size();
	if(k.empty()) return k;
	for(int i=0;i<len;i++){
		b[i+1].l=k[i].l;
		b[i+1].r=k[i].r;
	}
	vector<zxc> w;
	w.clear();
	sort(b+1,b+len+1,amp);
	w.push_back(dl);
	int num=0;
	for(int i=1;i<=len;i++){
		if(b[i].l>w[num].r+1){
			num++;
			w.push_back(b[i]);
		}
		else if(b[i].r>w[num].r){
			w[num].r=b[i].r;
		}
	}
	return w;
}
vector<zxc> merge(vector<zxc> s1,vector<zxc> s2){
	if(s1.empty()) return s2;
	vector<zxc> w;
	w.clear();
	for(int i=0;i<s1.size();i++){
		for(int j=0;j<s2.size();j++){
			zxc s3;
			s3.l=s1[i].l+s2[j].l;
			s3.r=s1[i].r+s2[j].r;
			w.push_back(s3);
		}
	}
	w=qc(w);
	return w;
}
void build(int p,int l,int r){
	s[p].push_back(dl);
	tr[p].l=l,tr[p].r=r;
	if(l==r){
		zxc fk;
		fk.l=a[l].l,fk.r=a[l].r;
		s[p].push_back(fk);
		return;
	}
	int mid=(l+r)/2;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	s[p]=merge(s[p*2],s[p*2+1]);
}
void change(int p,int wh,int l,int r){
	if(tr[p].l==tr[p].r){
		zxc fk;
		fk.l=l,fk.r=r;
		s[p].clear();
		s[p].push_back(dl);
		s[p].push_back(fk);
		return;
	}
	int mid=(tr[p].l+tr[p].r)/2;
	if(wh<=mid) change(p*2,wh,l,r);
	else change(p*2+1,wh,l,r);
	s[p]=merge(s[p*2],s[p*2+1]);
}
vector<zxc> ans;
void ask(int p,int l,int r){
	if(tr[p].l>=l &&tr[p].r<=r){
		ans=merge(ans,s[p]);
		return;
	}
	int mid=(tr[p].l+tr[p].r)/2;
	if(l<=mid) ask(p*2,l,r);
	if(r>mid) ask(p*2+1,l,r);
}
signed main(){
	dl.l=0,dl.r=0;
	int n,q;
	scanf("%lld%lld",&n,&q);
	for(int i=1;i<=n;i++){
		scanf("%lld%lld",&a[i].l,&a[i].r);
	}
	build(1,1,n);
	for(int i=1;i<=q;i++){
		int op;
		scanf("%lld",&op);
		if(op==2){
			int x,y;
			scanf("%lld%lld",&x,&y);
			ans.clear();
			ask(1,x,y);
			ans=qc(ans);
			int lhx=0;
			for(int j=0;j<ans.size();j++){
				lhx+=ans[j].r-ans[j].l+1;
			}
			printf("%lld\n",lhx);
		} 
		else{
			int x,y,z;
			scanf("%lld%lld%lld",&x,&y,&z);
			change(1,x,y,z);
		}
	}
} 
**都看到这了,还不点个赞。**
/*
3 5
2 3
1 1
3 4
2 1 1
2 1 2
2 1 3
1 2 1 5
2 1 3
*/
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,R,P;
int dp[215][215][25][25];
int v[250],a[250];
int mgml(int x,int p){
	int ans=1;
	while(p){
		if(p&1) ans=ans*x;
		x=x*x;
		p>>=1;
	}
	return ans;
}
int val(int x,int k){
	return v[k]*mgml(P,x-1);
}
void solve(int l,int r){
	for(int k=1;k<=m;k++){
		for(int i=l;i<=r;i++){
			if(a[i]==k){
				dp[l][r][1][k]=max(dp[l][r][1][k],dp[l][i-1][0][0]+dp[i+1][r][0][0]);
			}
		}
	}
	for(int x=2;x<=R;x++){
		for(int k=1;k<=m;k++){
			for(int i=l;i<=r;i++){
				dp[l][r][x][k]=max(dp[l][r][x][k],dp[l][i][x-1][k]+dp[i+1][r][x-1][k]);
			}
		}
	}
	
	int res1=0,res2=0;
	for(int i=l;i<=r-1;i++){
		res1=max(res1,dp[l][i][0][0]+dp[i+1][r][0][0]);
	}
	for(int x=1;x<=R;x++){
		for(int k=1;k<=m;k++){  
			res2=max(res2,dp[l][r][x][k]+val(x,k));
		}
	}
	dp[l][r][0][0]=max(res1,res2);
}
signed main(){
	for(int i=0;i<=210;i++){
		for(int j=0;j<=210;j++){
			for(int k=1;k<=22;k++){
				for(int l=1;l<=22;l++){
					dp[i][j][k][l]=-1145141919280;
				}
			}
		}
	}
	scanf("%lld%lld%lld%lld",&n,&m,&R,&P);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]); 
	}
	for(int i=1;i<=m;i++){
		scanf("%lld",&v[i]);
	}
	for(int len=1;len<=n;len++){
		for(int l=1,r=l+len-1;r<=n;l++,r++){
			solve(l,r);
		}
	}
	cout<<dp[1][n][0][0]<<endl;
}
/*
7 3 4 3
1 3 2 3 2 3 3
1 2 3
*/
**都看到这了,还不点个赞。**
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5005;
const int mod=998244353;
int n;
int dp[N][N][2];
int s[N];
int jc[N];
void init(){
	jc[0]=1;
	for(int i=1;i<=n+10;i++){
		jc[i]=jc[i-1]*i%mod;
	}
} 
signed main(){
	scanf("%lld",&n);
	int op=0;
	for(int i=1;i<n;i++){
		scanf("%lld",&s[i]);
	}
	dp[1][0][0]=dp[1][1][1]=1;
	for(int i=2;i<=n;i++){
		for(int j=0;j<=i;j++){
			if(s[i-1]){
				dp[i][j][0]+=dp[i-1][j][0];
				dp[i][j][1]+=dp[i-1][j][1];//此位为0
				if(j>0){
					dp[i][j][1]+=dp[i-1][j-1][0]+dp[i-1][j-1][1];//此位为1 
				}
			}
			else{
				dp[i][j][0]+=dp[i-1][j][0]+dp[i-1][j][1];//此为为0
				if(j>0){
					dp[i][j][1]+=dp[i-1][j-1][1];
					dp[i][j][0]+=dp[i-1][j-1][0];
				}
			}
			dp[i][j][0]%=mod;
			dp[i][j][1]%=mod;
		}
	}
	init();
	int ans=0;
	for(int i=1;i<=n;i++){
		int sum=(dp[n][i][1]*jc[i]%mod*jc[n-i]%mod+mod-dp[n][i-1][1]*jc[i-1]%mod*jc[n-i+1]%mod)%mod*(n-i+1)%mod;
		ans+=sum;
		ans%=mod;
	}
	printf("%lld",ans);
}

csp模拟13#

s#

如果这个理解不了你还可以这样计算答案, f[n][i][1]i!(ni)! 表示表示 ret>=ni+1 的方案, f[n][i+1][1] 表示 ret>=n+1 的方案,做差就可以得出 ret=ni+1 的方案数,再乘上 ni+1 就可以得到 ni+1 的贡献,累加即可。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5005;
const int mod=998244353;
int n;
int dp[N][N][2];
int s[N];
int jc[N];
void init(){
	jc[0]=1;
	for(int i=1;i<=n+10;i++){
		jc[i]=jc[i-1]*i%mod;
	}
} 
signed main(){
	scanf("%lld",&n);
	int op=0;
	for(int i=1;i<n;i++){
		scanf("%lld",&s[i]);
	}
	dp[1][0][0]=dp[1][1][1]=1;
	for(int i=2;i<=n;i++){
		for(int j=0;j<=i;j++){
			if(s[i-1]){
				dp[i][j][0]+=dp[i-1][j][0];
				dp[i][j][1]+=dp[i-1][j][1];//此位为0
				if(j>0){
					dp[i][j][1]+=dp[i-1][j-1][0]+dp[i-1][j-1][1];//此位为1 
				}
			}
			else{
				dp[i][j][0]+=dp[i-1][j][0]+dp[i-1][j][1];//此为为0
				if(j>0){
					dp[i][j][1]+=dp[i-1][j-1][1];
					dp[i][j][0]+=dp[i-1][j-1][0];
				}
			}
			dp[i][j][0]%=mod;
			dp[i][j][1]%=mod;
		}
	}
	init();
	int ans=0;
	for(int i=1;i<=n;i++){
		int sum=(dp[n][i][1]*jc[i]%mod*jc[n-i]%mod+mod-dp[n][i-1][1]*jc[i-1]%mod*jc[n-i+1]%mod)%mod*(n-i+1)%mod;
		ans+=sum;
		ans%=mod;
	}
	printf("%lld",ans);
}

p#

然后就是进行线段树维护一个区间的左右树的直径的两个端点,合并时考虑六种情况,注意赋初值

这里做一下引流

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2*1e5+10;
int head[N*2],ver[N*2],nex[N*2],tot=0;
void add(int x,int y){
	ver[++tot]=y,nex[tot]=head[x],head[x]=tot;
	ver[++tot]=x,nex[tot]=head[y],head[y]=tot;
}
int fa[N],top[N],size[N],d[N],son[N];
void dfs1(int x,int f){
	d[x]=d[f]+1;
	fa[x]=f;
	size[x]=1;
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(y==f) continue;
		dfs1(y,x);
		size[x]+=size[y];
		if(size[son[x]]<size[y]){
			son[x]=y;
		}
	}
}

void dfs2(int x,int tp){
	top[x]=tp;
	if(son[x]) dfs2(son[x],tp);
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(y==fa[x]) continue;
		if(y!=son[x]) dfs2(y,y);
	}
}

int ask_lca(int x,int y){
	int fx=top[x],fy=top[y];
	while(fx!=fy){
		if(d[fx]>d[fy]){
			x=fa[fx],fx=top[x];
		}
		else{
			y=fa[fy],fy=top[y];
		}
	}
	if(d[x]<d[y]) return x;
	else return y;
}

struct asd{
	int l,r,ls,rs,sum;
}tr[N*4];

void build(int p,int l,int r){
	tr[p].l=l,tr[p].r=r;
	tr[p].ls=tr[p].rs=0;
	if(l==r){
		tr[p].ls=tr[p].rs=0;
		return;
	}
	int mid=(l+r)/2;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
}

asd jjq(asd s1,asd s2){
	int id1=0,id2=0;
	int dis=-1;
	asd z;
	if(s1.ls && s2.ls){
		int lca=ask_lca(s1.ls,s2.ls);
		int dist=d[s1.ls]+d[s2.ls]-2*d[lca];
		if(dis<dist){
			dis=dist;
			id1=s1.ls,id2=s2.ls;
		}
	}
	if(s1.rs && s2.rs){
		int lca=ask_lca(s1.rs,s2.rs);
		int dist=d[s1.rs]+d[s2.rs]-2*d[lca];
		if(dis<dist){
			dis=dist;
			id1=s1.rs,id2=s2.rs;
		}
	}
	if(s1.ls && s1.rs){
		int lca=ask_lca(s1.ls,s1.rs);
		int dist=d[s1.ls]+d[s1.rs]-2*d[lca];
		if(dis<dist){
			dis=dist;
			id1=s1.ls,id2=s1.rs;
		}
	}
	if(s2.ls && s2.rs){
		int lca=ask_lca(s2.ls,s2.rs);
		int dist=d[s2.ls]+d[s2.rs]-2*d[lca];
		if(dis<dist){
			dis=dist;
			id1=s2.ls,id2=s2.rs;
		}
	}
	if(s1.ls && s2.rs){
		int lca=ask_lca(s1.ls,s2.rs);
		int dist=d[s1.ls]+d[s2.rs]-2*d[lca];
		if(dis<dist){
			dis=dist;
			id1=s1.ls,id2=s2.rs;
		}
	}
	if(s1.rs && s2.ls){
		int lca=ask_lca(s1.rs,s2.ls);
		int dist=d[s1.rs]+d[s2.ls]-2*d[lca];
		if(dis<dist){
			dis=dist;
			id1=s1.rs,id2=s2.ls;
		}
	}
	z.ls=id1,z.rs=id2;
	z.l=s1.l,z.r=s2.r;
	z.sum=s1.sum+s2.sum;
	return z;
}

void change(int p,int wh){
	if(tr[p].l==tr[p].r){
		tr[p].sum^=1;
		if(tr[p].sum){
			tr[p].ls=tr[p].rs=tr[p].l;
		}
		else{
			tr[p].ls=tr[p].rs=0;
		}
		return;
	}
	int mid=(tr[p].l+tr[p].r)/2;
	if(wh<=mid) change(p*2,wh);
	else change(p*2+1,wh);
	tr[p]=jjq(tr[p*2],tr[p*2+1]);
}

asd ask(int p,int l,int r){
	if(tr[p].l>=l && tr[p].r<=r) return tr[p];
	int mid=(tr[p].l+tr[p].r)/2;
	asd s1,s2,s3;
	s1.sum=0,s1.ls=0,s1.rs=0,s2.ls=0,s2.rs=0,s2.sum=0;
	s1.l=s1.r=s2.l=s2.r=0;
	if(l<=mid) s1=ask(p*2,l,r);
	if(r>mid) s2=ask(p*2+1,l,r);
	s3=jjq(s1,s2);
	return s3;
}

int ask_sum(int p,int l,int r){
	if(tr[p].l>=l && tr[p].r<=r) return tr[p].sum;
	int mid=(tr[p].l+tr[p].r)/2;
	int ans=0;
	if(l<=mid) ans+=ask_sum(p*2,l,r);
	if(r>mid) ans+=ask_sum(p*2+1,l,r);
	return ans;
}
signed main(){
	int n,m;
	int op1=0;
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
	}
	dfs1(1,0);
	dfs2(1,1);
	build(1,1,n);
	for(int i=1;i<=m;i++){
		int op;
		scanf("%d",&op);
		if(op==1){
			int x;
			scanf("%d",&x);
			change(1,x);
		}
		else{
			int l,r;
			scanf("%d%d",&l,&r);
			asd s=ask(1,l,r);
			int lca=ask_lca(s.ls,s.rs);
			int ans=ask_sum(1,l,r);
			if(ans==0){
				cout<<"-1"<<endl;
				continue;
			}
			if(ans==1){
				cout<<"0"<<endl;
				continue;
			}
			printf("%d\n",d[s.ls]+d[s.rs]-2*d[lca]);
		}
	}
}
/*
5 5

1 2
1 3
3 4
3 5

1 1
1 3
2 4 5
1 4
2 1 4

*/

m#

然后进行预处理卡时间卡空间,还有朱老师介绍的光速幂,复杂度预处理 O((n)),查询O(1)

csp模拟12#

便#

这道题思想很不错,利用线段树维护转移矩阵,感觉官方题解很详细

还有就是各种优化,比如实际矩阵是一个三角矩阵,所以优化一半。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
const int mod=1e9+7;

inline int read(){
	int x(0);bool f(0);char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar()) f^=ch=='-';
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
	return f?x=-x:x;
}
inline void write(int x){
	x<0?x=-x,putchar('-'):0;static short Sta[50],top(0);
	do{Sta[++top]=x%10;x/=10;}while(x);
	while(top) putchar(Sta[top--]|48);
	putchar('\n');
}
int n,a[N];
struct asd{
	int l,r;
}tr[N*4];
struct qwe{
	int l,r,s[12][12];
}t[N*4];
int sum=0,ny;
int mgml(int x,int p){
	int ans=1;
	while(p){
		if(p&1) ans=(ans*x)%mod;
		x=(x*x)%mod;
		p>>=1;
	}
	return ans;
}
qwe mul(qwe s1,qwe s2){
	qwe z;
	memset(z.s,0,sizeof(z.s));
	z.l=s1.r;
	z.r=s2.l;
	for(int i=1;i<=s1.r;++i){
		for(int j=1;j<=s2.l;++j){
			int mi=min(s1.l,j);
			for(int k=i;k<=mi;++k){
				z.s[j][i]+=s1.s[k][i]*s2.s[j][k];
			}	
			z.s[j][i]%=mod;
		}
	}
	return z;
}
void build(int p,int l,int r){
	tr[p].l=l,tr[p].r=r;
	t[p].l=11,t[p].r=11;
	if(l==r){
		for(int i=1;i<=11;++i){
			t[p].s[i][i]=1;
		}
		t[p].s[2][1]=a[1];
		t[p].s[3][2]=a[2];
		t[p].s[4][3]=t[p].s[5][4]=t[p].s[10][9]=a[3];
		t[p].s[6][5]=t[p].s[8][7]=a[4];
		t[p].s[7][6]=a[5];
		t[p].s[9][8]=a[6];
		t[p].s[11][10]=a[7];
		return;
	}
	int mid=(l+r)/2;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	t[p]=mul(t[p*2],t[p*2+1]);
}
void change(int p,int wh,char s){
	if(tr[p].l==tr[p].r){
		memset(t[p].s,0,sizeof(t[p].s));
		t[p].l=t[p].r=11;
		for(int i=1;i<=11;++i){
			t[p].s[i][i]=1;
		}
		if(s=='h') t[p].s[2][1]=1;
		if(s=='e') t[p].s[3][2]=1;
		if(s=='l') t[p].s[4][3]=t[p].s[5][4]=t[p].s[10][9]=1;
		if(s=='o') t[p].s[6][5]=t[p].s[8][7]=1;
		if(s=='w') t[p].s[7][6]=1;
		if(s=='r') t[p].s[9][8]=1;
		if(s=='d') t[p].s[11][10]=1;
		return;
	}
	int mid=(tr[p].l+tr[p].r)/2;
	if(wh<=mid) change(p*2,wh,s);
	else change(p*2+1,wh,s);
	t[p]=mul(t[p*2],t[p*2+1]);
}
qwe kkk;
void ask(int p,int l,int r){
	if(tr[p].l>=l && tr[p].r<=r){
		kkk=mul(kkk,t[p]);
		return;
	}
	int mid=(tr[p].l+tr[p].r)/2;
	if(l<=mid) ask(p*2,l,r);
	if(r>mid) ask(p*2+1,l,r);
	return;
}
signed main(){
	n=read();
	for(int i=1;i<=7;++i){
 		a[i]=read();
		sum=(sum+a[i])%mod;
	}
	ny=mgml(sum,mod-2);
	for(int i=1;i<=7;++i){
		a[i]=a[i]*ny%mod;
	}
	build(1,1,n);
	int q;
	q=read();
	for(int i=1;i<=q;++i){
		int op;
		op=read();
		if(op==1){
			int wh;
			char s;
			wh=read();
			cin>>s;
			change(1,wh,s);
		}
		
		else{
			int l,r;
			l=read(),r=read();
			qwe hx;
			memset(hx.s,0,sizeof(hx.s));
			hx.l=11,hx.r=1;
			hx.s[1][1]=1;
			memset(kkk.s,0,sizeof(kkk.s));
			kkk.l=kkk.r=11;
			for(int l=1;l<=11;l++){
				kkk.s[l][l]=1;
			}
			ask(1,l,r);
			hx=mul(hx,kkk);
			write(hx.s[11][1]);
		}
	}
}
/*
11
1 1 1 1 1 1 1
16
1 1 h
2 2 11
2 2 11
1 2 e
1 3 l
1 4 l
1 5 l
2 1 11
1 6 o
1 7 w
2 2 11
1 8 o
1 9 r
1 10 l
1 11 d
2 1 11

*/

csp模拟15#

Team Work#

先学斯特林数

注意分情况求解。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e7+10;
const int mod=1e9+7;
int n,k;
int s2[5005][5005];
int f[N],inv[N];
int base[N];
int mgml(int x,int p){
	int ans=1;
	while(p){
		if(p&1) ans=(ans*x)%mod;
		x=(x*x)%mod;
		p>>=1;
	}
	return ans;
}
int c(int x,int y){
	int ans=1;
	for(int i=y+1;i<=x;i++){
		ans=(ans*i)%mod;
	}
	return ans;
}
void init(){
	f[0]=1;
	inv[0]=1;
	for(int i=1;i<=n;i++){
		f[i]=f[i-1]*i%mod;
		inv[i]=mgml(f[i],mod-2);
	}
}
int cc(int x,int y){
	return f[x]*inv[y]%mod*inv[x-y]%mod;
}
signed main(){
	scanf("%lld%lld",&n,&k);
	if(k>=5002){
		init();
		int sum=0;
		for(int i=1;i<=n;i++){
			sum=(sum+cc(n,i)*mgml(i,k)%mod)%mod;
		}
		printf("%lld",sum);
		return 0;
	}
	s2[0][0]=1;
	for(int i=1;i<=k;i++){
		for(int j=1;j<=k;j++){
			s2[i][j]=(s2[i-1][j-1]+j*s2[i-1][j]%mod)%mod;
		}
	}
	int ans=0;
	for(int i=1;i<=k;i++){
		int sum;
		sum=s2[k][i]*c(n,n-i)%mod*mgml(2,n-i)%mod;
		ans=(ans+sum)%mod;
	}
	cout<<ans<<endl;
	
}

Make Equal#

太出生了,最后交了一个带 freopen 的代码,挂 40pts

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int a[N],n;
int num[80];
struct asd{
	int data,id;
}c[N];
int dp[80][N];
bool amp(asd x,asd y){
	return x.data>y.data;
}
signed main(){
//	freopen("4.in","r",stdin);
	scanf("%lld",&n);
	int ma=-1;
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		for(int j=1;j<=58;j++){
			if(a[i]&(1ll<<(j-1))){
				num[j]++;
				ma=max(ma,j);
			}
		}
	}
	memset(dp,0x7f,sizeof(dp));
	dp[0][0]=0; 
	for(int i=0;i<=ma+1;i++){
		memset(c,0,sizeof(c));
		for(int j=1;j<=n;j++){
			c[j].data=(a[j]&((1ll<<(i-1))-1));
			c[j].id=j;
		}
		sort(c+1,c+n+1,amp);
		int k=0;
		for(int j=0;j<=n;j++){
			if(a[c[j].id]&(1ll<<(i-1))) k++;
			if(c[j].data==c[j+1].data && j!=n && j!=0) continue;
			dp[i+1][k]=min(dp[i+1][k],dp[i][j]+n-num[i]-j+2*k);//全为1 
			dp[i+1][num[i]+j-k]=min(dp[i+1][num[i]+j-k],dp[i][j]+num[i]+j-2*k);
		}
	}
	cout<<dp[ma+1][0];
} 

都看到这了,还不点个赞。

作者:bloss

出处:https://www.cnblogs.com/jinjiaqioi/p/17575758.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   _bloss  阅读(58)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu