THUC和PKUC的补题记录


P5368 [PKUSC2018]真实排名

老早做过了,这次就不补了,大简单题,随便二分一下(好像还可以双指针)就行了。

代码

无。

P5369 [PKUSC2018]最大前缀和

这是一道很巧妙的题目。

你考虑到如果一个前缀是最大前缀,它一定满足这样一个性质:可以拆分成前后两个子区间,然后满足前一个子区间的最大子段和(前缀和)就是全选,后一个区间的最大前缀和为负。

我们考虑用两个东西来维护这两种序列,定义 \(f_i\) 表示当所选集合为 \(i\) 的时候,有 \(f_i\) 种方案可以排列出满足前一个性质的子序列,定义 \(g_i\) 为当前所选集合为 \(i\) 时,有 \(g_i\) 种方案可以排列出后一种子序列。

我们考虑每次在第一种子序列前添一个数,在第二种子序列后添一个数,转移方程如下:

\[f_{S}=\sum_{i\in S}f_{S-\{i\}}\cdot[sum_{S-\{i\}}\ge 0]\\ g_{S}=(\sum_{i\in S}g_{S-\{i\}})\cdot[sum_{S}<0] \]

最后枚举一下哪些点在 \(f\) 中,哪些点在 \(g\) 中,统计下答案即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=21;
const int MOD=998244353;
int n,a[N],f[1<<N],g[1<<N],sum[1<<N],res;
signed main(){
	cin>>n;
	for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
	for(int i=0;i<(1<<n);++i){
		for(int j=1;j<=n;++j)
		if((i>>(j-1))&1) sum[i]+=a[j];
	}
	f[0]=g[0]=1;
	for(int i=0;i<(1<<n);++i){
		for(int j=1;j<=n;++j){
			if((i>>(j-1))&1) continue;
			if(sum[i]>=0) f[i|(1<<(j-1))]+=f[i],f[i|(1<<(j-1))]%=MOD;
			if(sum[i|(1<<(j-1))]<0) g[i|(1<<(j-1))]+=g[i],g[i|(1<<(j-1))]%=MOD;
		}
	}
	for(int i=0;i<(1<<n);++i) res+=(sum[i]%MOD+MOD)*f[i]%MOD*g[(1<<n)-1-i]%MOD,res%=MOD;
	return printf("%lld\n",res),0;
}

P4681 [THUSC2015]平方运算

你可以证明打表,发现循环节的 \(\text{lcm}\) 是不会超 \(60\) 的,然后你直接暴力修改未到循环节的部分,然后在线段树上处理一下循环节即可。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,K=75;
int n,m,MOD,a[N];
struct Data{int len,now,sum[K];};
int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
int lcm(int a,int b){return a/gcd(a,b)*b;}
Data operator + (Data a,Data b){
	Data res;
	if(a.len==-1||b.len==-1) return res.len=-1,res;
	res.len=lcm(a.len,b.len),res.now=1;
	for(int i=1,c=a.now,d=b.now;i<=res.len;++i)
	res.sum[i]=a.sum[c]+b.sum[d],c=c%a.len+1,d=d%b.len+1;
	return res;
}
Data get(int x){
	Data res;map<int,int> mp;res.sum[1]=x;
	for(int i=1;;++i,res.sum[i]=res.sum[i-1]*res.sum[i-1]%MOD){
		if(mp[res.sum[i]]){
			if(mp[res.sum[i]]==1) return res.len=i-1,res.now=1,res;
			else return res.len=-1,res;
		}
		mp[res.sum[i]]=i;
	}
	return res;
}
struct Seg_Tree{
	struct Node{int num,tag;Data data;}tr[N<<2];
	void up(int u){
		tr[u].num=0;
		if(tr[u<<1].data.len>0) tr[u].num+=tr[u<<1].data.sum[tr[u<<1].data.now];
		else tr[u].num+=tr[u<<1].num;
		if(tr[u<<1|1].data.len>0) tr[u].num+=tr[u<<1|1].data.sum[tr[u<<1|1].data.now];
		else tr[u].num+=tr[u<<1|1].num;
		tr[u].data=tr[u<<1].data+tr[u<<1|1].data;
	}
	void update(int u,int z){
		tr[u].data.now=(tr[u].data.now+z-1)%tr[u].data.len+1,tr[u].tag+=z;
	}
	void down(int u){
		update(u<<1,tr[u].tag),update(u<<1|1,tr[u].tag),tr[u].tag=0;
	}
	void build(int u,int l,int r,int a[]){
		if(l==r) return tr[u].num=a[l],tr[u].data=get(tr[u].num),void();
		int mid=(l+r)>>1;
		build(u<<1,l,mid,a),build(u<<1|1,mid+1,r,a);
		return up(u);
	}
	void modify(int u,int l,int r,int x,int y){
		if(x<=l&&r<=y){
			if(tr[u].data.len>0) return update(u,1);
			if(l==r) return tr[u].num=tr[u].num*tr[u].num%MOD,tr[u].data=get(tr[u].num),void();
		}
		int mid=(l+r)>>1;down(u);
		if(x<=mid) modify(u<<1,l,mid,x,y);
		if(y>mid) modify(u<<1|1,mid+1,r,x,y);
		return up(u);
	}
	int query(int u,int l,int r,int x,int y){
		if(x<=l&&r<=y){
			if(tr[u].data.len>0) return tr[u].data.sum[tr[u].data.now];
			else return tr[u].num;
		}
		int mid=(l+r)>>1,res=0;down(u);
		if(x<=mid) res+=query(u<<1,l,mid,x,y);
		if(y>mid) res+=query(u<<1|1,mid+1,r,x,y);
		return res;
	}
}t;
int main(){
	cin>>n>>m>>MOD;
	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
	t.build(1,1,n,a);
	for(int i=1;i<=m;++i){
		int opt,l,r;scanf("%d%d%d",&opt,&l,&r);
		if(opt==1) t.modify(1,1,n,l,r);
		else printf("%d\n",t.query(1,1,n,l,r));
	}
}

P5794 [THUSC2015]解密运算

一道简单思维题,想一想就会做了。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m,a[N],b[N],go[N];
queue<int> q[N];
int main(){
	cin>>n>>m;
	for(int i=1;i<=n+1;++i) scanf("%d",&a[i]),b[i]=a[i];
	sort(b+1,b+2+n);
	for(int i=1;i<=n+1;++i) q[a[i]].push(i);
	for(int i=1;i<=n+1;++i) go[i]=q[b[i]].front(),q[b[i]].pop();
	int tmp=-1;
	for(int i=1;i<=n+1;++i) if(!a[i]) tmp=i;
	for(int i=1;i<=n;++i) printf("%d ",b[tmp]),tmp=go[tmp];
	return 0;
}

P5795 [THUSC2015]异或运算

可持久化 \(\text{Trie}\) 树上二分,一只 \(\log_2\)

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5,M=3e5+5,Q=505;
int n,m,q,a[N],b[M];
struct Per_Trie{
	int tot,rt[M];
	struct Node{int size,son[2];}tr[M*32];
	void insert(int u,int v,int x){
		tr[v]=tr[u];tr[v].size++;
		for(int i=30;i>=0;--i){
			int tmp=((x>>i)&1);
			tr[v].son[tmp]=++tot;
			v=tr[v].son[tmp],u=tr[u].son[tmp];
			tr[v]=tr[u],tr[v].size++;
		}
	}
	int query(vector<int> u,vector<int> v,vector<int> a,int k){
		int res=0;
		for(int i=30;i>=0;--i){
			int tmp=0;
			for(int j=0;j<(int)u.size();++j){
				int tag=((a[j]>>i)&1);
				tmp+=tr[tr[v[j]].son[tag]].size-tr[tr[u[j]].son[tag]].size;
			}
			if(k<=tmp){
				for(int j=0;j<(int)u.size();++j){
					int tag=((a[j]>>i)&1);
					v[j]=tr[v[j]].son[tag],u[j]=tr[u[j]].son[tag];
				}
			}
			else{
				k-=tmp,res|=(1<<i);
				for(int j=0;j<(int)u.size();++j){
					int tag=((a[j]>>i)&1);
					v[j]=tr[v[j]].son[!tag],u[j]=tr[u[j]].son[!tag];
				}
			}
		}
		return res;
	}
}t;
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
	for(int i=1;i<=m;++i) scanf("%d",&b[i]);
	for(int i=1;i<=m;++i) t.insert(t.rt[i-1],t.rt[i]=++t.tot,b[i]);
	cin>>q;
	for(int i=1;i<=q;++i){
		int u,d,l,r,k;vector<int> U,V,bag;
		scanf("%d%d%d%d%d",&u,&d,&l,&r,&k);
		k=(d-u+1)*(r-l+1)-k+1;
		for(int j=u;j<=d;++j){
			U.push_back(t.rt[l-1]);
			V.push_back(t.rt[r]);
			bag.push_back(a[j]);
		}
		printf("%d\n",t.query(U,V,bag,k));
	}
	return 0;
}

P5335 [THUSC2016]补退选

可持久化 \(\text{Trie}\) + 二分 。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,lstans=0;
struct Trie{
	int tot,rt[N];
	struct Node{int size,maxn,go[10];}tr[N*60];
	void insert(int u,int v,string s,int z){
		tr[v]=tr[u],tr[v].size+=z,tr[v].maxn=max(tr[u].maxn,tr[v].size);
		for(int i=0;i<(int)s.length();++i){
			tr[v].go[s[i]-'a']=++tot,u=tr[u].go[s[i]-'a'],v=tr[v].go[s[i]-'a'];
			tr[v]=tr[u],tr[v].size+=z,tr[v].maxn=max(tr[u].maxn,tr[v].size);
		}
	}
	int query(int u,string s){
		for(int i=0;i<(int)s.length();++i)
		u=tr[u].go[s[i]-'a'];
		return tr[u].maxn;
	}
}t;
int main(){
	cin>>n;
	for(int i=1;i<=n;++i){
		int opt,a,b,c;string s;
		cin>>opt>>s;
		if(opt==1) t.insert(t.rt[i-1],t.rt[i]=++t.tot,s,1);
		else if(opt==2) t.insert(t.rt[i-1],t.rt[i]=++t.tot,s,-1);
		else{
			t.rt[i]=t.rt[i-1];
			cin>>a>>b>>c;
			int l=1,r=i,tmp=(1ll*a*abs(lstans)+b)%c;lstans=-1;
			while(l<=r){
				int mid=(l+r)>>1;
				if(t.query(t.rt[mid],s)>tmp) lstans=mid,r=mid-1;
				else l=mid+1;
			}
			printf("%d\n",lstans);
		}
	}
	return 0;
}

P5336 [THUSC2016]成绩单

一道很精妙的 \(\text{dp}\) 题,一开始码了直接一个 \(40pts\) 的假做法,拿了 \(40pts\) ,但是非常不能理解。

\(\text{zkdxl}\) 直接一组数据拍我脸上说,你这算法不是随便 \(\text{hack}\) ,我直接爪巴。

正解应该是要考虑到很多剩余区间的合并,我们定义 \(f_{l,r,c,d}\) 表示区间 \([l,r]\) 中剩余部分的最大值是 \(d\) ,最小值是 \(c\) 的最小代价,定义 \(g_{l,r}\) 表示区间 \([l,r]\) 全部分发的最小代价,我们可以得到三种转移。

\[f_{l,r,\min(c,a_r),\max(d,a_r)}=\min f_{l,r-1,c,d}\\ f_{l,r,c,d}=\min_{k=l}^{r-1}f_{l,k,c,d}+g_{k+1,r}\\ g_{l,r}=\min_{c=1}^m\min_{d=c}^mf_{l,r,c,d}+a+b\times(d-c)^2 \]

三种转移应该都比较好理解。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=55;
int n,m,x,y,a[N],b[N];map<int,int> mp;
int minn[N][N],maxn[N][N];
int f[N][N][N][N],g[N][N],res=1e18+7;
signed main(){
	cin>>n>>x>>y,m=n;
	for(int i=1;i<=n;++i) scanf("%lld",&a[i]),b[i]=a[i];
	sort(b+1,b+1+m),m=unique(b+1,b+1+m)-b-1;
	for(int i=1;i<=m;++i) mp[b[i]]=i;
	for(int i=1;i<=n;++i) a[i]=mp[a[i]];
	for(int i=0;i<N;++i){
		for(int j=0;j<N;++j){
			for(int c=0;c<N;++c){
				for(int d=0;d<N;++d)
				f[i][j][c][d]=1e18+7;
			}
			g[i][j]=1e18+7;
		}
	}
	for(int i=1;i<=n;++i) f[i][i][a[i]][a[i]]=0,g[i][i]=x;
	for(int len=2;len<=n;++len){
		for(int l=1,r=len;r<=n;++l,++r){
			for(int c=1;c<=m;++c){
				for(int d=c;d<=m;++d){
					f[l][r][min(c,a[r])][max(d,a[r])]=min(f[l][r][min(c,a[r])][max(d,a[r])],f[l][r-1][c][d]);
					for(int k=l;k<r;++k) f[l][r][c][d]=min(f[l][r][c][d],f[l][k][c][d]+g[k+1][r]);
				}
			}
			for(int c=1;c<=m;++c){
				for(int d=c;d<=m;++d)
				g[l][r]=min(g[l][r],f[l][r][c][d]+x+y*(b[d]-b[c])*(b[d]-b[c]));
			}
			// printf("%lld %lld %lld\n",l,r,g[l][r]);
		}
	}
	return printf("%lld\n",g[1][n]),0;
}

P7450 [THUSCH2017] 巧克力

这是一道大神仙题。

首先你要会一个算法叫做斯坦纳树,如果你不会这个一切都白搭。

下面假设你已经会了,或者你去学完了之后来了。

首先我们假设对于整个矩阵的颜色只有 \(k\) 个,相当于每一种颜色都需要选上,这个时候我们考虑怎么做?

我们考虑到斯坦纳树的知识,其实它也是可以处理颜色块的,你只需要在初始化的时候每一个点对应颜色都初始化一遍就行了。

但是斯坦纳树算法只能保证巧克力大小最小,不能保证在大小相同的情况下中位数也最小,所以对于中位数我们需要再加一个二分去找。具体操作就是对于大于当前二分值的设为 \(-1\) 小于等于的设为 \(1\) ,然后结合进斯坦纳树算法的搜索过程,找到最小的能够使得和大于等于 \(0\) 的一个值。

然后对于整个矩形较多的我们如何处理?我们可以随机给每种颜色分配 \(1\)\(k\) 的权值,可以证明(自己证明去),在随机个 \(100\) 左右之后正确率就足以通过题目了。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=233,K=6;
int n,m,k,a[N][N],c[N][N],id[N];bool mp[N][N];
int go[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int f[1<<K][N][N],g[1<<K][N][N],res1,res2;
int data(int x,int tmp){return x<=tmp?1:-1;}
struct Data{int x,y,dis;};
bool operator < (Data a,Data b){return a.dis>b.dis;}
priority_queue<Data> q;bool vis[N][N];
void dijkstra(int s,int tmp){
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j)
		vis[i][j]=false;
	}
	while(!q.empty()){
		int x=q.top().x,y=q.top().y;q.pop();
		if(vis[x][y]) continue;else vis[x][y]=true;
		for(int i=0;i<4;++i){
			int X=x+go[i][0],Y=y+go[i][1];if(mp[X][Y]) continue;
			int tmp1=f[s][x][y]+1,tmp2=g[s][x][y]+data(a[X][Y],tmp);
			if(f[s][X][Y]==tmp1&&g[s][X][Y]<tmp2) g[s][X][Y]=tmp2;
			else if(f[s][X][Y]>tmp1) f[s][X][Y]=tmp1,g[s][X][Y]=tmp2,q.push((Data){X,Y,tmp1});
		}
	}
}
pair<int,int> cal(int tmp){
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			for(int s=1;s<(1<<k);++s)
			f[s][i][j]=1e9+7;
		}
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			if(c[i][j]<0) continue;
			f[1<<(id[c[i][j]]-1)][i][j]=1;
			g[1<<(id[c[i][j]]-1)][i][j]=data(a[i][j],tmp);
		}
	}
	for(int s=1;s<(1<<k);++s){
		for(int i=1;i<=n;++i){
			for(int j=1;j<=m;++j){
				for(int subs=s&(s-1);subs;subs=s&(subs-1)){
					int tmp1=f[subs][i][j]+f[s-subs][i][j]-1;
					int tmp2=g[subs][i][j]+g[s-subs][i][j]-data(a[i][j],tmp);
					if(f[s][i][j]==tmp1&&g[s][i][j]<tmp2) g[s][i][j]=tmp2;
					else if(f[s][i][j]>tmp1) f[s][i][j]=tmp1,g[s][i][j]=tmp2;
				}
				if(f[s][i][j]<=n*m) q.push((Data){i,j,f[s][i][j]});
			}
		}
		dijkstra(s,tmp);
	}
	int res1=1e9+7,res2=-1;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			int tmp1=f[(1<<k)-1][i][j],tmp2=g[(1<<k)-1][i][j];
			if(res1==tmp1&&res2<tmp2) res2=tmp2;
			else if(res1>tmp1) res1=tmp1,res2=tmp2;
		}
	}
	return make_pair(res1,res2);
}
void Mid_search(){
	for(int i=1;i<=n*m;++i) id[i]=0;
	for(int i=1;i<=k;++i) id[rand()%(n*m)+1]=i;
	for(int i=1;i<=n*m;++i) if(!id[i]) id[i]=rand()%k+1;
	int L=0,R=1e6,ans=R;
	while(L<=R){
		int mid=(L+R)>>1;pair<int,int> tmp=cal(mid);
		if(tmp.second>=0) R=mid-1,ans=mid;else L=mid+1;
	}
	pair<int,int> tmp=cal(ans);
	if(res1==tmp.first&&res2>ans) res2=ans;
	else if(res1>tmp.first) res1=tmp.first,res2=ans;
}
void solve(){
	cin>>n>>m>>k;
	memset(mp,0,sizeof(mp));
	for(int i=1;i<=n;++i) mp[i][0]=mp[i][m+1]=true;
	for(int i=1;i<=m;++i) mp[0][i]=mp[n+1][i]=true;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			scanf("%d",&c[i][j]);
			if(c[i][j]<0) mp[i][j]=true;
		}
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j)
		scanf("%d",&a[i][j]);
	}
	int T=93;res1=1e9+7,res2=-1;
	while(T--) Mid_search();
	if(res1>=1e9+7) printf("-1 -1\n");
	else printf("%d %d\n",res1,res2);
}
int main(){
	srand(time(NULL));
	int T;cin>>T;
	while(T--) solve();
}

P7451 [THUSCH2017] 杜老师

这道题目是一道很巧妙的猜结论题。

我们首先考虑对于完全平方数的性质,是满足所有质因数都为偶数的情况,这个东西如果转换为二进制表示每一个质因数的奇偶状态的话,就可以用线性基来维护,这个就等同于这题了。

但是我们发现这道题目直接这么搞肯定是不行的,但是我们可以通过维护出来每一个数 \(\le\sqrt n\) 的质因数奇偶性,剩下的最多只有一个质因数了。

相当于现在的复杂度是 \(O(\sum_{i=1}^T(R_i-L_i)\log_2(R_i-L_i)\sqrt n)\) ,肯定是不行的。

但是我们可以通过暴力打表找规律证明,得到当 \(R_i-L_i\ge 2\sqrt n\) 的时候,存在于这段区间内的质因数是一定会存在主元的,然后我们直接枚举质因数判断即可。

复杂度非常吓人,但是是对的。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e7+5,M=505;
const int MOD=998244353;
int L,R,cnt=0,sum[N];
map<int,bool> mp;
vector<int> pri;
int ksm[N];
struct Linear_basis{
	int tot,mp[M];
	bitset<M> data[M];
	void init(){
		tot=0,memset(mp,0,sizeof(mp));
	}
	void insert(bitset<M> x){
		for(int i=0;i<M;++i){
			if(!x[i]) continue;
			if(mp[i]) x^=data[mp[i]];
			else return data[++tot]=x,mp[i]=tot,void();
		}
	}
}lb;
void subtask1(){
	cnt=0,lb.init(),mp.clear();
	for(int i=L;i<=R;++i){
		int tmp=i;bitset<M> x;x.reset();
		for(int j=0;j<M;++j){
			while(tmp%pri[j]==0)
			tmp/=pri[j],x[j]=(x[j])?0:1;
		}
		if(tmp>1&&!mp[tmp]) cnt++,mp[tmp]=true;
		else lb.insert(x);
	}
	printf("%lld\n",ksm[R-L+1-lb.tot-cnt]);
}
void subtask2(){
	cnt=0;
	for(int i=0;i<sum[R];++i) cnt+=((L-1)/pri[i]!=R/pri[i]);
	printf("%lld\n",ksm[R-L+1-cnt]);
}
void solve(int T){
	cin>>L>>R;
	if(R-L+1<=(sqrt(N)*2)) return subtask1();
	else return subtask2();
}
signed main(){
	ksm[0]=1;
	for(int i=1;i<N;++i) ksm[i]=(ksm[i-1]<<1)%MOD;
	for(int i=2;i<N;++i){
		if(!sum[i]) pri.push_back(i);
		for(int j=0;j<(int)pri.size();++j){
			if(i*pri[j]>=N) break;
			sum[i*pri[j]]=true;
			if(i%pri[j]==0) break;
		}
	}
	for(int i=2;i<N;++i) sum[i]=sum[i-1]+(!sum[i]);
	int T;cin>>T;
	while(T--) solve(T);
	return 0;
}

P7452 [THUSCH2017] 换桌

我网络流水平大退化了,已经看不出来了。。。

然后你发现看出来是网络流之后就可以直接搞了,然后用线段树优化一下建图,考虑先选桌子再选位子就行了。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=305,M=15;
const int INF=1e9+7;
int n,m,L[N][M],R[N][M];
int tot,from,to,res1=0,res2=0;
int out[N][M],in[N][M][2];
struct Edge{int nxt,to,flow,cost;};
vector<Edge> e;int fir[N*M*20];
void add(int u,int v,int x,int y){
	e.push_back((Edge){fir[u],v,x,y}),fir[u]=e.size()-1;
	e.push_back((Edge){fir[v],u,0,-y}),fir[v]=e.size()-1;
}
struct Seg_Tree{
	struct Node{int id;}tr[N<<2];
	void build(int u,int l,int r,int x,int tag){
		tr[u].id=++tot;
		if(l==r) return add(tr[u].id,in[l][x][1],INF,2*(tag?l:-l));
		int mid=(l+r)>>1;
		build(u<<1,l,mid,x,tag),add(tr[u].id,tr[u<<1].id,INF,0);
		build(u<<1|1,mid+1,r,x,tag),add(tr[u].id,tr[u<<1|1].id,INF,0);
	}
	void add_edge(int u,int l,int r,int x,int y,int i,int j,int k){
		if(x>y) return ;
		if(x<=l&&r<=y) return add(out[i][j],tr[u].id,INF,k);
		int mid=(l+r)>>1;
		if(x<=mid) add_edge(u<<1,l,mid,x,y,i,j,k);
		if(y>mid) add_edge(u<<1|1,mid+1,r,x,y,i,j,k);
	}
}t[2][M];
int cur[N*M*20],dis[N*M*20];
queue<int> q;bool vis[N*M*20];
bool bfs(){
	for(int i=1;i<=tot;++i) dis[i]=INF,cur[i]=fir[i];
	dis[from]=0,q.push(from),vis[from]=true;
	while(!q.empty()){
		int u=q.front();q.pop(),vis[u]=false;
		for(int i=fir[u];i>=0;i=e[i].nxt){
			int v=e[i].to;
			if(!e[i].flow||dis[v]<=dis[u]+e[i].cost) continue;
			dis[v]=dis[u]+e[i].cost;if(!vis[v]) q.push(v),vis[v]=true;
		}
	}
	return dis[to]!=INF;
}
int dfs(int u,int flow){
	if(u==to) return flow;
	int res=0;vis[u]=true;
	for(int i=cur[u];i>=0&&flow;i=e[i].nxt){
		int v=e[i].to;cur[u]=i;
		if(!e[i].flow||dis[v]!=dis[u]+e[i].cost||vis[v]) continue;
		int tmp=dfs(v,min(flow,e[i].flow));
		e[i].flow-=tmp,e[i^1].flow+=tmp,flow-=tmp,res+=tmp;
	}
	return vis[u]=false,res;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j)
		scanf("%d",&L[i][j]),L[i][j]++;
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j)
		scanf("%d",&R[i][j]),R[i][j]++;
	}
	from=++tot,to=++tot;
	memset(fir,-1,sizeof(fir));
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			out[i][j]=++tot,add(from,out[i][j],1,0);
			in[i][j][0]=++tot,add(in[i][j][0],to,1,0);
			in[i][j][1]=++tot;
		}
		for(int j=1;j<=m;++j){
			for(int k=1;k<=m;++k){
				int tmp=min(abs(j-k),m-abs(j-k));
				add(in[i][j][1],in[i][k][0],1,tmp);
			}
		}
	}
	for(int i=1;i<=m;++i){
		t[0][i].build(1,1,n,i,0);
		t[1][i].build(1,1,n,i,1);
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			if(i<L[i][j]) t[1][j].add_edge(1,1,n,L[i][j],R[i][j],i,j,-i*2);
			else if(i>R[i][j]) t[0][j].add_edge(1,1,n,L[i][j],R[i][j],i,j,i*2);
			else{
				t[0][j].add_edge(1,1,n,L[i][j],i,i,j,i*2);
				t[1][j].add_edge(1,1,n,i,R[i][j],i,j,-i*2);
			}
		}
	}
	while(bfs()){
		int tmp=dfs(from,INF);
		res1+=tmp,res2+=tmp*dis[to];
	}
	if(res1<n*m) printf("no solution\n");
	else printf("%d\n",res2);
	return 0;
}

P7453 [THUSCH2017] 大魔法师

应该是比较裸的一题了,直接用线段树结合矩阵乘法直接维护即可。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5,MOD=998244353;
int n,m,opt,l,r,v;
struct Matrix{
    int n,m,h[2][5];
    inline void print(){
        printf("%d %d %d\n",h[1][1],h[1][2],h[1][3]);
    }
};
struct Matrixhhh{
    int n,m,h[5][5];
    inline void Set0(int a){
        n=m=a,memset(h,0,sizeof(h));
        for(int i=1;i<=a;++i) h[i][i]=1;
    }
};
Matrix operator + (const Matrix &a,const Matrix &b){
    Matrix ans1;ans1.n=a.n,ans1.m=b.m;
    for(register int i=1;i<=a.n;++i){
        for(register int j=1;j<=b.m;++j)
        ans1.h[i][j]=(a.h[i][j]+b.h[i][j])%MOD;
    }
    return ans1;
}
Matrix operator * (const Matrix &a,const Matrixhhh &b){
    Matrix ans1;ans1.n=a.n,ans1.m=b.m;
    memset(ans1.h,0,sizeof(ans1.h));
    for(register int i=1;i<=a.n;++i){
        for(register int j=1;j<=b.m;++j){
            for(register int k=1;k<=a.m;++k)
            ans1.h[i][j]+=1ll*a.h[i][k]*b.h[k][j]%MOD,ans1.h[i][j]%=MOD;
        }
    }
    return ans1;
}
Matrixhhh operator * (const Matrixhhh &a,const Matrixhhh &b){
    Matrixhhh ans2;ans2.n=a.n,ans2.m=b.m;
    memset(ans2.h,0,sizeof(ans2.h));
    for(register int i=1;i<=a.n;++i){
        for(register int j=1;j<=b.m;++j){
            for(register int k=1;k<=a.m;++k)
            ans2.h[i][j]+=1ll*a.h[i][k]*b.h[k][j]%MOD,ans2.h[i][j]%=MOD;
        }
    }
    return ans2;
}
Matrix s[N];Matrixhhh go[8];
inline void init(){
    for(register int i=1;i<=6;++i) go[i].n=go[i].m=4;
    go[1].h[1][1]=go[1].h[2][2]=go[1].h[3][3]=go[1].h[4][4]=go[1].h[2][1]=1;
    go[2].h[1][1]=go[2].h[2][2]=go[2].h[3][3]=go[2].h[4][4]=go[2].h[3][2]=1;
    go[3].h[1][1]=go[3].h[2][2]=go[3].h[3][3]=go[3].h[4][4]=go[3].h[1][3]=1;
    return ;
}
struct Node{
    Matrix data;
    Matrixhhh tag;
}tr[N<<2];
inline void up(int p){
    tr[p].data=tr[p<<1].data+tr[p<<1|1].data;
}
inline void down(int p,int l,int r){
    int mid=(l+r)>>1;
    tr[p<<1].data=tr[p<<1].data*tr[p].tag;
    tr[p<<1].tag=tr[p<<1].tag*tr[p].tag;
    tr[p<<1|1].data=tr[p<<1|1].data*tr[p].tag;
    tr[p<<1|1].tag=tr[p<<1|1].tag*tr[p].tag;
    tr[p].tag.Set0(4);
    return ;
}
inline void build(int p,int l,int r){
    tr[p].tag.Set0(4);
    if(l==r) return tr[p].data=s[l],void();
    int mid=(l+r)>>1;
    build(p<<1,l,mid),build(p<<1|1,mid+1,r);
    return up(p);
}
inline void add(int p,int l,int r,int x,int y,int opt){
    if(x<=l&&r<=y){
        tr[p].data=tr[p].data*go[opt];
        tr[p].tag=tr[p].tag*go[opt];
        return ;
    }
    int mid=(l+r)>>1;down(p,l,r);
    if(x<=mid) add(p<<1,l,mid,x,y,opt);
    if(y>=mid+1) add(p<<1|1,mid+1,r,x,y,opt);
    return up(p);
}
inline Matrix query(int p,int l,int r,int x,int y){
    if(x<=l&&r<=y) return tr[p].data;
    int mid=(l+r)>>1;down(p,l,r);
    Matrix res;res.n=1,res.m=4;
    memset(res.h,0,sizeof(res.h));
    if(x<=mid) res=res+query(p<<1,l,mid,x,y);
    if(y>=mid+1) res=res+query(p<<1|1,mid+1,r,x,y);
    return res;
}
inline int read(){
    char c=getchar();int x=0;
    while(c<'0'||'9'<c) c=getchar();
    while('0'<=c&&c<='9') x*=10,x+=c-'0',c=getchar();
    return x;
}
signed main(){
    init(),n=read();
    for(register int i=1;i<=n;i+=4){
        s[i].n=1,s[i].m=4;
        s[i].h[1][1]=read();
        s[i].h[1][2]=read();
        s[i].h[1][3]=read();
        s[i].h[1][4]=1;
        if(i+1>n) break;
        s[i+1].n=1;
        s[i+1].m=4;
        s[i+1].h[1][1]=read();
        s[i+1].h[1][2]=read();
        s[i+1].h[1][3]=read();
        s[i+1].h[1][4]=1;
        if(i+2>n) break;
        s[i+2].n=1,s[i+2].m=4;
        s[i+2].h[1][1]=read();
        s[i+2].h[1][2]=read();
        s[i+2].h[1][3]=read();
        s[i+2].h[1][4]=1;
        if(i+3>n) break;
        s[i+3].n=1,s[i+3].m=4;
        s[i+3].h[1][1]=read();
        s[i+3].h[1][2]=read();
        s[i+3].h[1][3]=read();
        s[i+3].h[1][4]=1;
    }
    build(1,1,n);
    m=read();
    for(register int i=1;i<=m;++i){
        opt=read(),l=read(),r=read();
        if(4<=opt&&opt<=6){
            v=read();
            switch(opt){
                case 4:{
                    go[4].h[1][1]=go[4].h[2][2]=go[4].h[3][3]=go[4].h[4][4]=1;
                    go[4].h[4][1]=v;
                    break;
                }
                case 5:{
                    go[5].h[1][1]=go[5].h[3][3]=go[5].h[4][4]=1;
                    go[5].h[2][2]=v;
                    break;
                }
                case 6:{
                    go[6].h[1][1]=go[6].h[2][2]=go[6].h[4][4]=1;
                    go[6].h[4][3]=v;
                    break;
                }
            }
        }
        if(opt==7) query(1,1,n,l,r).print();
        else add(1,1,n,l,r,opt);
    }
}


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM