1. 程式人生 > 其它 >多執行緒排序-v0

多執行緒排序-v0

AGC043

B 123 Triangle

題意

給定長為 \(n\) 的序列 \(a\)。有遞推關係 \(f_{k,x}=|f_{k-1,x}-f_{k-1,x+1}|(k>1,x\le n-k+1),f_{1,x}=a_x\)。求 \(f_{n,1}\)

資料範圍\(n\le 10^6,1\le a_i\le 3\)

view solution

solution

先把 \(n=1\) 判掉,然後 \(a_i\in[0,2]\)。注意到 \(1\) 的特殊性:如果原序列存在 \(1\),那麼最後答案一定是 \(0,1​\) 中的一個。

對於 \(f_i\),如果存在一個 \(1\) 其左邊/右邊不是 \(1\)

,那麼在 \(f_{i+1}\) 中一定存在至少一個 \(1\)。否則所有數都是 \(1\)\(f_{i+1}\) 中都是 \(0\)

所以如果有 \(1\) 的話,只需要判斷答案的奇偶性。注意到 \(|a-b|\bmod 2=a+b \bmod 2\)\(a_i\)\(f_{n,0}\) 的貢獻是 \(\binom{n-1}{i-1}\)。所以答案是

\[\sum_{i=1}^n a_i\binom{n-1}{i-1}\bmod 2 \]

使用 Lucas 定理即可。

對於沒有 \(1\) 的情況,把 \(2\) 視為 \(1\) 再跑即可。

view code
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
inline int binom(int n,int m){return (n&m)==m?1:0;}
int n,a[N];
char s[N];
int main(){
	scanf("%d",&n);
	scanf("%s",s+1);
	for(int i=1;i<=n;++i)a[i]=s[i]-'0';
	if(n==1){
		printf("%d\n",a[1]);
		return 0;
	}
	for(int i=1;i<n;++i)a[i]=abs(a[i]-a[i+1]);
	--n;
	bool flag=0;
	for(int i=1;i<=n;++i){
		if(a[i]==1)flag=1;
	}
	int key=flag?1:2;
	int sum=0;
	for(int i=1;i<=n;++i)
		if(a[i]==key)sum^=binom(n-1,i-1);
	printf("%d\n",sum?key:0);
	return 0;
}

C Giant Graph

題意

給定三個簡單無向圖 \(G_1,G_2,G_3\),點數均為 \(n\)

另根據這三張圖構造一個有 \(n^3\) 個點的圖 \(G\),圖 \(G\)

  • \(\forall (u,v)\in G_1,a,b\in[1,n]\),連邊 \((u,a,b),(v,a,b)\)
  • \(\forall (u,v)\in G_2,a,b\in[1,n]\),連邊 \((a,u,b),(a,b,v)\)
  • \(\forall (u,v)\in G_3,a,b\in[1,n]\),連邊 \((a,b,u),(a,b,v)\)

對於 \(G\) 中的任意一個點 \((x,y,z)\)

,定義其點權為 \(10^{18(x+y+z)}\)

\(G\) 的最大權獨立集的大小模 \(998244353\) 的值。

資料範圍\(n,m\le 10^5\)

view solution

solution

把原問題轉博弈題。

首先因為一個點的權值很大,所以我們肯定優先選 \(x+y+z\) 大的,這樣與 \((x,y,z)\) 有邊相連的點肯定不能選。

我們把要選的點視為必敗態,這樣所有能一步走到必敗態的點就是必勝態。答案是所有必勝態的點的權值之和。

把每張圖邊定向,從小的連向大的,這樣變成一個博弈的 DAG,並求出所有點的 SG 函式。如果 \((x,y,z)\) 滿足 \(sg_1(x)\oplus sg_2(y)\oplus sg_3(z)=0\),那麼點 \((x,y,z)\) 是必敗態,即我們需要選的點。注意到 \(sg\) 值是 \(O(\sqrt{n})\) 級別的,預處理每張圖中,\(sg(a)=i\) 的所有 \(a\)\(10^{18a}\) 之和,然後列舉 \(sg_1(x)\)\(sg_2(y)\) 即可。複雜度 \(O(n+m)\)

view code
#include <bits/stdc++.h>
using namespace std;
inline int read(){
	int s=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
	return s*f;
}
const int N=1e5+5,mod=998244353;
inline int quick_pow(int a,int b){
	int ret=1;
	for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)ret=1ll*ret*a%mod;
	return ret;
}
const int pw=quick_pow(10,18);
int n;
struct Graph{
	vector<int> e[N];
	int cnt[N],sg[N],m,mx;
	bool vis[N];
	void dfs(int u){
		if(sg[u]!=-1)return;
		for(int v:e[u])
			dfs(v);
		for(int v:e[u])
			vis[sg[v]]=1;
		for(int i=0;;++i)if(!vis[i]){
			sg[u]=i;
			break;
		}
		for(int v:e[u])
			vis[sg[v]]=0;
	}
	void work(){
		m=read();
		for(int i=1,u,v;i<=m;++i){
			u=read();v=read();
			if(u>v)swap(u,v);
			e[u].push_back(v);
		}
		memset(sg+1,-1,n<<2);
		for(int i=1;i<=n;++i){
			dfs(i);cnt[sg[i]]=(cnt[sg[i]]+quick_pow(pw,i))%mod;
			mx=max(mx,sg[i]);
		}
	}
}G1,G2,G3;
int main(){
	n=read();
	G1.work();
	G2.work();
	G3.work();
	int ans=0;
	for(int i=0;i<=G1.mx;++i)if(G1.cnt[i])
		for(int j=0;j<=G2.mx;++j)if(G2.cnt[j])
			if(G3.cnt[i^j])ans=(ans+1ll*G1.cnt[i]*G2.cnt[j]%mod*G3.cnt[i^j])%mod;
	printf("%d\n",ans);
	return 0;
}

D Merge Triplets

題意

\(n\) 個大小為 \(3\) 的序列,總共 \(3n\) 個元素,這 \(3n\) 個元素構成一個 \(1\sim 3n\) 的排列。

另有一個排列 \(P\),每次選出所有非空序列中的第一個元素中最小的一個,把這個元素放到 \(P\) 的末尾,並把它從其所在序列中刪除。一直操作直到所有序列為空,即 \(P\) 變成一個 \(1\sim 3n\) 的排列。

對於所有生成 \(n\) 個序列的方式,求能生成多少個不同的排列 \(P\),答案取模。

資料範圍\(n\le 2000\)

view solution ### solution

對於一個大小為 \(3\) 的序列,它在 \(P\) 中的位置一定是下面三種情況之一:

  • 一個長為為 \(3\) 的連續段
  • 兩個連續段,長度為 \(1+2/2+1\)
  • 三個連續段,長度為 \(1,1,1\)

也就是,只要一個排列 \(P\) 滿足以下限制,它一定能被構造出來:

能被字首最大值分為若干段,每段的長度 \(\le 3\),且 \(2\) 的數量 \(\le 1\) 的數量。

DP,\(f_{i,j}\) 表示考慮完前 \(i\) 個數,\(1\) 的數量 \(-2\) 的數量 \(=j\) 的方案數。

複雜度 \(O(n^2)\)

view code
#include <bits/stdc++.h>
using namespace std;
const int N=2005;
int C[N*3][3],mod;
inline int add(int a,int b){return (a+b>=mod)?a+b-mod:a+b;}
inline void init(int n){
	C[0][0]=1;
	for(int i=1;i<=n;++i){
		C[i][0]=1;
		for(int j=1;j<3;++j)C[i][j]=add(C[i-1][j-1],C[i-1][j]);
	}
}
struct Val{
	int f[6*N];
	inline int& operator[](int x){return f[x+3*N];}
};
int n;
Val f[N*3];
int main(){
	cin>>n>>mod;
	init(n*3);
	f[0][0]=1;
	for(int i=1;i<=3*n;++i){
		for(int j=1;j<=3&&j<=i;++j){
			int v=C[i-1][j-1];
			if(j==3)v=2ll*v%mod;
			for(int k=-i+1;k<i;++k){
				if(j==1)f[i][k+1]=(f[i][k+1]+1ll*v*f[i-j][k])%mod;
				else if(j==2)f[i][k-1]=(f[i][k-1]+1ll*v*f[i-j][k])%mod;
				else f[i][k]=(f[i][k]+1ll*v*f[i-j][k])%mod;
			}
		}
	}
	int ans=0;
	for(int i=0;i<=3*n;++i)ans=add(ans,f[3*n][i]);
	printf("%d\n",ans);
	return 0;
}

E Topology

題意

平面上有 \(n\) 個點,分別位於 \((i+\frac{1}{2},\frac{1}{2})(i\in[0,n-1])\),以及一個封閉曲線 \(C\)。給定 \(2^n\) 個狀態,每個狀態 \(f_S\) 表示在考慮 \(S\) 集合內的所有點時,曲線 \(C\) 能否在不接觸 \(S\) 集合內的點,移動到所有點的縱座標都 \(<0\) 的位置。

請你根據 \(f\) 構造一個滿足條件的 \(C\),或者判定無解。

資料範圍\(n\le 8\)

view solution

首先容易發現,\(f\) 具有傳遞性,即如果 \(f_{S}=1\),那麼對於 \(T\subseteq S,f_{T}=1\);如果 \(f_{S}=0\),那麼對於 \(S\subseteq T,f_{T}=0\)。如果違反了傳遞性顯然無解。

我們現在找到所有的 \(S\),滿足所有 \(S\) 的子集都能解出來,\(S\)\(S\) 的所有超集都不能被解出來。如果我們的構造滿足:對於 \(S\) 無法解出,而刪掉任何一個點都能解出,那麼我們把所有 \(S\) 的構造連到同一個點上把它們拼起來,就滿足了題面的所有限制。

構造之前,先考慮 spj 怎麼寫:從封閉曲線的一個點出發走一圈,如果經過點 \(i\) 的上方,往序列裡寫下一個 \(u_i\);如果經過點 \(i\) 的下方,寫下一個 \(t_i\)。如果出現了連續的 \(u_i,u_i\) 或者 \(t_i,t_i\),那麼這兩步可以刪掉,不會影響 \(i\) 是否被 \(C\) 包住,也就不會影響 \(C\) 是否能解出。如果一直把整個序列都刪完了,那麼不存在任何一個點被包住,即 \(C\) 可以解出,否則 \(C\) 無法解出。

會了判定之後遞迴構造方案(方案要滿足:對於 \(S\) 無法解出,而刪掉任何一個點都能解出):

  • 如果當前集合內只有 \(1\) 個點,把這個點包一圈即可
  • 先把最靠左的 \(i\) 去掉,設 \(T\) 表示 \(S'=S\setminus \{i\}\) 的方案,那麼我們構造 \(u_iTu_it_iT't_i\)\(T'\) 表示把 \(T\) 中的方案倒序之後的方案。容易發現,如果任何一個點被刪掉(刪掉它的所有 \(u_i,t_i\)),那麼整個序列就能被刪完;否則整個序列不能被刪掉任何一個元素。
view code
#include <bits/stdc++.h>
using namespace std;
const int N=10;
#define pr pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define pb push_back
#define ins(x,y) ret.pb(mp(x,y))
inline vector<pr> solve(int s,int pre){
	int x=__builtin_ctz(s);
	vector<pr> ret;
	for(int i=pre+1;i<=x;++i)
		ins(i,0);
	if(__builtin_popcount(s)==1){
		ins(x+1,0);ins(x+1,1);
		ins(x,1);ins(x,0);
		for(int i=x;i>pre;--i)
			ins(i-1,0);
		return ret;
	}
	ins(x+1,0);
	vector<pr> T=solve(s^(1<<x),x+1);
	for(pr p:T)ret.pb(p);
	ins(x,0);
	ins(x,1);
	ins(x+1,1);
	ins(x+1,0);
	T.pop_back();
	reverse(T.begin(),T.end());
	for(pr p:T)ret.pb(p);
	ins(x+1,0);
	ins(x+1,1);
	ins(x,1);
	ins(x,0);
	for(int i=x-1;i>=pre;--i)ins(i,0);
	return ret;
}
int n;
char s[1<<N];
int a[N],f[1<<N];
vector<pr> ans;
int main(){
	scanf("%d",&n);
	scanf("%s",s);
	int tot=(1<<n);
	for(int i=0;i<tot;++i){
		f[i]=s[i]-'0';
		for(int j=0;j<n;++j)
			if(i&(1<<j)){
				if(f[i]==1&&f[i^(1<<j)]==0){
					puts("Impossible");
					return 0;
				}
			}
	}
	ans.pb(mp(0,0));
	for(int i=1;i<tot;++i){
		if(!f[i]){
			bool flag=1;
			for(int j=0;j<n;++j)if(i&(1<<j)){
				if(!f[i^(1<<j)])flag=0;
			}
			if(flag){
				vector<pr> cur=solve(i,0);
				for(pr p:cur)ans.pb(p);
			}
		}
	}
	puts("Possible");
	printf("%d\n",(int)ans.size()-1);
	for(pr p:ans)printf("%d %d\n",p.fi,p.se);
	return 0;
}

F Jewelry Box

題意

\(N\) 個珠寶商店。

每個商店賣 \(K_i\) 珠寶,第 \(i\) 個商店的第 \(j(1\le j\le K_i)\) 珠寶擁有三個獨立的屬性 \((S,P,C)\) 依次表示重量,價格,數量。

現在有 \(Q\) 組詢問,每次給定一個 \(A_i\),詢問能否夠構造 \(A_i\) 個“珠寶盒”,如果可以則輸出最小的花費(即購買的珠寶的價格之和)否則輸出 \(-1\)

一個“珠寶盒”是一個包含 \(N\) 個珠寶的盒子,且滿足如下條件:

  • 盒子內部的第 \(i\) 個珠寶從第 \(i\) 個珠寶商店處購買。
  • 滿足 \(M\) 條約束:
    • 對於第 \(i\) 條約束:此盒子內第 \(V_i\) 珠寶的重量應當不超過\(U_i\) 個珠寶的重量 \(+ W_i\)

資料範圍

\(N,K_i\le 30,S_{i,j}\le 10^9,P_{i,j}\le 30,C_{i,j}\le 10^{12},M\le 50,Q\le 10^5,A_i\le 3\times 10^{13},W_i\le 10^9\)

view soluiton

參見 sxTQX 的部落格 線性規劃對偶問題

view code
#include <bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
	int s=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
	return s*f;
}
namespace Flow{
	const int M=1e5+5,N=1e5+5,inf=1e17;
	struct Edge{int to,next,flow,cost;}e[M];
	int dis[N],head[N],cur[N],tot,ecnt=1,h[N],p[N];
	inline void adde(int u,int v,int flow,int cost){
		e[++ecnt]=(Edge){v,head[u],flow,cost};head[u]=ecnt;
		e[++ecnt]=(Edge){u,head[v],0,-cost};head[v]=ecnt;
	}
	bool inq[N];
	bool SPFA(int s,int t){
		memset(dis+1,0x3f,tot<<3);
		dis[s]=0;
		queue<int> q;
		q.push(s);
		while(!q.empty()){
			int u=q.front();q.pop();
			inq[u]=0;
			cur[u]=head[u];
			for(int i=head[u],v;i;i=e[i].next){
				if(!e[i].flow)continue;
				v=e[i].to;
				if(dis[v]>dis[u]+e[i].cost){
					dis[v]=dis[u]+e[i].cost;
					if(!inq[v])q.push(v),inq[v]=1;
				}
			}
		}
		memcpy(p+1,dis+1,tot<<3);
		return dis[t]<=inf;
	}
	bool dijkstra(int s,int t){
		for(int i=1;i<=tot;++i)h[i]+=p[i];
		priority_queue<pair<int,int> > q;
		q.push(make_pair(0,s));
		memset(dis+1,0x3f,tot<<3);
		memset(inq+1,0,tot);
		dis[s]=0;
		while(!q.empty()){
			int u=q.top().second;q.pop();
			if(inq[u])continue;
			inq[u]=1;
			cur[u]=head[u];
			for(int i=head[u],v;i;i=e[i].next){
				if(!e[i].flow)continue;
				v=e[i].to;
				if(dis[v]>dis[u]+e[i].cost-h[v]+h[u]){
					dis[v]=dis[u]+e[i].cost-h[v]+h[u];
					q.push(make_pair(-dis[v],v));
				}
			}
		}
		memcpy(p+1,dis+1,tot<<3);
		return dis[t]<=inf;
		
	}
	bool vis[N];
	int dinic(int u,int t,int flow){
		if(u==t)
			return flow;
		vis[u]=1;
		int ret=0,f;
		for(int&i=cur[u];i;i=e[i].next){
			int v=e[i].to;
			if(!e[i].flow||dis[v]!=dis[u]+e[i].cost-h[v]+h[u]||vis[v])continue;
			f=dinic(v,t,min(flow,e[i].flow));
			e[i].flow-=f;e[i^1].flow+=f;
			ret+=f;flow-=f;
		}
		vis[u]=0;
		if(f)dis[u]=inf;
		return ret;
	}
}
const int N=55,inf=1e17;
int s[N][N],p[N][N],c[N][N],s1[N],p1[N],c1[N],k[N],n,id[N][N],tot;
int cnt,flow[N*N],cost[N*N],F[N*N],m,per[N];
inline bool cmp(int x,int y){
	return s1[x]<s1[y];
}
signed main(){
	n=read();
	int S=++tot,T=++tot;
	for(int i=1;i<=n;++i){
		k[i]=read();
		for(int j=1;j<=k[i];++j){
			s1[j]=read();
			p1[j]=read();
			c1[j]=read();
			if(j>1)id[i][j]=++tot;
			else id[i][j]=S;
			per[j]=j;
		}
		sort(per+1,per+1+k[i],cmp);
		for(int j=1;j<=k[i];++j){
			s[i][j]=s1[per[j]];
			p[i][j]=p1[per[j]];
			c[i][j]=c1[per[j]];
		}
		id[i][k[i]+1]=++tot;
		for(int j=2;j<=k[i]+1;++j){
			Flow::adde(id[i][j-1],id[i][j],p[i][j-1],0);
			Flow::adde(id[i][j-1],id[i][j],inf,c[i][j-1]);
			Flow::adde(id[i][j],id[i][j-1],inf,0);
		}
		Flow::adde(id[i][k[i]+1],T,inf,0);
	}
	Flow::tot=tot;
	m=read();
	while(m--){
		int u,v,w;
		u=read();v=read();w=read();
		int flag=1;
		for(int i=1;i<=k[v];++i){
			while(flag<=k[u]&&s[u][flag]+w<s[v][i])++flag;
			Flow::adde(id[v][i],id[u][flag],inf,0);
		}
	}
	int mf=0,mc=0;
	for(Flow::SPFA(S,T);Flow::dijkstra(S,T);){
		int f=Flow::dinic(S,T,inf);
		flow[++cnt]=mf;
		cost[cnt]=mc;
		int d=Flow::dis[T]+Flow::h[T]-Flow::h[S];
		F[cnt]=d;
		if((__int128)f*d>=inf)break;
		mf+=f;
		mc+=f*d;
	}
	int q=read();
	while(q--){
		int a=read();
		int i=lower_bound(F+1,F+cnt+1,a)-F;
		if(i>cnt)
			puts("-1");
		else printf("%lld\n",flow[i]*a-cost[i]);
	}
	return 0;
}