多執行緒排序-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)\)
求 \(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;
}