W
e
l
c
o
m
e
: )

赛生新

8题榜首,what can i say,被一万个人 n+1

A

子序列匹配,容易做到 \(O(nm)\)

Code
#include <bits/stdc++.h>
using namespace std;
char s[105];
//CCNU HUST HZAU WHU
signed main(){
	scanf("%s", s+1);
	int n=strlen(s+1);
	long long f[4]={0, 0, 0, 0};
	for(int i=1; i<=n; ++i){
		if(s[i]=='C'){
			f[1]+=f[0];++f[0];
		}
		else if(s[i]=='N') f[2]+=f[1];
		else if(s[i]=='U') f[3]+=f[2];
	}
	string ans="CCNU";long long mx=f[3];
	f[0]=f[1]=f[2]=f[3]=0;
	for(int i=1; i<=n; ++i){
		if(s[i]=='H') ++f[0];
		else if(s[i]=='U') f[1]+=f[0];
		else if(s[i]=='S') f[2]+=f[1];
		else if(s[i]=='T') f[3]+=f[2];
	}
	if(f[3]>mx) mx=f[3], ans="HUST";
	f[0]=f[1]=f[2]=f[3]=0;
	for(int i=1; i<=n; ++i){
		if(s[i]=='H') ++f[0];
		else if(s[i]=='Z') f[1]+=f[0];
		else if(s[i]=='A') f[2]+=f[1];
		else if(s[i]=='U') f[3]+=f[2];
	}
	if(f[3]>mx) mx=f[3], ans="HZAU";
	f[0]=f[1]=f[2]=f[3]=0;
	for(int i=1; i<=n; ++i){
		if(s[i]=='W') ++f[0];
		else if(s[i]=='H') f[1]+=f[0];
		else if(s[i]=='U') f[2]+=f[1];
	}
	if(f[2]>mx) mx=f[2], ans="WHU";
	f[0]=f[1]=f[2]=f[3]=0;
	for(int i=1; i<=n; ++i){
		if(s[i]=='W') ++f[0];
		else if(s[i]=='H') f[1]+=f[0];
		else if(s[i]=='U') f[2]+=f[1];
		else if(s[i]=='T') f[3]+=f[2];
	}
	if(f[3]>mx) mx=f[3], ans="HZAU";
	cout<<ans<<' '<<mx;
	return 0;
}

WHUT 打成 HZAU 都能过,不尊重了

L

按题意输出

Code
#include <bits/stdc++.h>
using namespace std;
signed main(){
	long long a, b, c;
	scanf("%lld%lld%lld", &a, &b, &c);
	printf("The paper you submitted have %lld Pros and %lld Cons, so I have %lld Questions for you.",
	a, b, c);
	return 0;
}

C

偷到手刹了

若在 \(l\) 之前经过,\(\operatorname{map}\) 统计最早经过时间。

若在 \(r\) 之后经过,则加上 \([l, r]\) 的差分量,此时另一个 \(\operatorname{map}\) 统计最晚经过时间。

Code
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int n, q, Dx[N], Dy[N];char s[N];
map < pair<int, int>, int > MN, MX;
signed main(){
	scanf("%d%d", &n, &q);
	scanf("%s", s+2);
	int dx=0, dy=0;
	MN[{dx, dy}]=1, MX[{dx, dy}]=1;
	for(int i=2; i<=n+1; ++i){
		if(s[i]=='L') --dx;
		if(s[i]=='R') ++dx;
		if(s[i]=='U') ++dy;
		if(s[i]=='D') --dy;
		Dx[i]=dx, Dy[i]=dy;
		if(!MN[{dx, dy}]) MN[{dx, dy}]=i;
		MX[{dx, dy}]=i; 
	}
	for(int i=1; i<=q; ++i){
		int l, r, x, y;
		scanf("%d%d%d%d", &l, &r, &x, &y);++l, ++r;
		if(MN[{x, y}]&&MN[{x, y}]<l){puts("YES");continue;}
		x+=Dx[r]-Dx[l-1], y+=Dy[r]-Dy[l-1];
		if(MX[{x, y}]>r){puts("YES");continue;}
		puts("NO");
	}
	return 0;
}

E

\(u\) 不是 \(v\) 的祖先,答案即为 \(n-siz_u\)

\(u\)\(v\) 的祖先,则找 \(v\to u\)\(u\) 的儿子

倍增/树剖都能做,长剖求个 \(k\) 级祖先应该可以线性

Code
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=3e5+5;
int n, q, siz[N], son[N], top[N], fa[N], dep[N];
vector <int> G[N];
void dfs(int x){
	siz[x]=1;
	for(auto v:G[x]) if(!dep[v]){
		dep[v]=dep[x]+1, fa[v]=x, dfs(v), siz[x]+=siz[v];
		if(siz[son[x]]<siz[v]) son[x]=v;
	} 
}
void dfs(int x, int rt){
	top[x]=rt;if(son[x]) dfs(son[x], rt);
	for(auto v:G[x]) if(!top[v]) dfs(v, v);
}
int LCA(int x, int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]>dep[top[y]]) x=fa[top[x]];
		else y=fa[top[y]];
	}return dep[x]<dep[y]?x:y;
}
signed main(){
	scanf("%d", &n);
	for(int i=1; i<n; ++i){
		int u, v;scanf("%d%d", &u, &v);
		G[u].pb(v), G[v].pb(u);
	}
	dfs(dep[1]=1);dfs(1, 1);
	scanf("%d", &q);
	for(int i=1; i<=q; ++i){
		int u, v;scanf("%d%d", &v, &u);
		int lca=LCA(u, v);
		if(lca!=v) printf("%d\n", n-siz[v]);
		else{
			int lst=0;
			while(top[u]!=top[v]) lst=top[u], u=fa[top[u]];
			if(dep[lst]==dep[v]+1) printf("%d\n", siz[lst]);
			else printf("%d\n", siz[son[v]]);
		}
	}
	return 0;
}

F

预处理每个 \(n\) 是否可行,暴力枚举 \(a, b\) 即可,复杂度 \(O(\sqrt{n}^2)=O(n)\)

Code
#include <bits/stdc++.h>
#define int long long
#define vi vector<int>
#define pb push_back
#define mp make_pair
#define st first
#define nd second
using namespace std;
typedef long long ll;
typedef pair <int, int> Pii;
const int INF=0x3f3f3f3f;
const int cp=998244353;
inline int mod(int x){return x+(x<0?cp:0)-(x>=cp?cp:0);}
inline void plust(int &x, int y){x=mod(x+y);return ;}
inline void minut(int &x, int y){x=mod(x-y);return ;}
inline int read(){
	char ch=getchar();int x=0, f=1;
	while(!isdigit(ch)){if(ch=='-') f=-1; ch=getchar();}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(int x){
    if(x<0) putchar('-'), x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
inline int ksm(int a, int b=cp-2){
	int ret=1;
	for(; b; b>>=1, a=1ll*a*a%cp)
		if(b&1) ret=1ll*ret*a%cp;
	return ret;
}
const int N=2e7+5;
bool can[N];
signed main(){
	for(int i=1; i*i<N; ++i)
		for(int j=i; i*i+j*j<N; ++j)
			can[i*i+j*j]=1;
	int q=read();while(q--) puts(can[read()]?"Yes":"No");
	return 0;
}

G

考虑前 \(2^{N-1}\) 次一直给 1,然后反复横跳 01010101 即可做到每次都错。

Code
#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
inline int read(){
	char ch=getchar();int x=0;
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0', ch=getchar();
	return x;
}
signed main(){
	int N=read(), len=read();
	int bit=1;
	for(int i=1; i<N; ++i){
		bit<<=1;
		if(bit>len) break;
	}
	if(bit>len){
		for(int i=1; i<=len; ++i) putchar('1');
		return 0;
	}
	for(int i=1; i<=bit; ++i) putchar('1');
	for(int i=bit+1; i<=len; ++i){
		if((i-bit-1)&1) putchar('1');
		else putchar('0');
	}
	return 0;
}

B

取偶数行偶数列为高地,高地显然要大的数,所以倒序把 \(n\dots 1\) 赋值,然后考虑高地旁边的数,也是倒序赋值,剩下的按顺序赋值,容易保证剩下的数不为高地

Code
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
inline int read(){
	char ch=getchar();int x=0;
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0', ch=getchar();
	return x;
}
bool is[105][105];
int a[105][105];
void slv(int n, int m, int k){
	for(int i=0; i<=n+1; ++i)
		for(int j=0; j<=m+1; ++j)
			is[i][j]=0, a[i][j]=0;
	if(n<=2||m<=2){
		if(k==0){
			puts("Yes");
			for(int i=1; i<=n; ++i, puts(""))
				for(int j=1; j<=m; ++j)
					printf("%d ", (i-1)*m+j);
		}
		else{
			puts("No"); 
		}
		return ;
	}
	int cnt=0;
	for(int i=2; i<n; i+=2)
		for(int j=2; j<m; j+=2)
			if(cnt<k) ++cnt, a[i][j]=n*m-cnt+1, is[i][j]=1;
	if(cnt<k){
		puts("No");
		return ;
	}
	for(int i=1; i<=n; ++i)
		for(int j=1; j<=m; ++j)
			if(!a[i][j])
				if(is[i-1][j]||is[i][j-1]||is[i+1][j]||is[i][j+1]||
				is[i-1][j-1]||is[i-1][j+1]||is[i+1][j-1]||is[i+1][j+1]){
					++cnt, a[i][j]=n*m-cnt+1;
				}
	for(int i=1; i<=n; ++i)
		for(int j=1; j<=m; ++j)
			if(!a[i][j]) ++cnt, a[i][j]=n*m-cnt+1;
		puts("Yes");
	for(int i=1; i<=n; ++i, puts(""))
		for(int j=1; j<=m; ++j) printf("%d ", a[i][j]);
//	int res=0;
//	for(int i=2; i<n; ++i)
//		for(int j=2; j<m; ++j)
//			if(a[i][j]){
//				int mx=0;
//				for(int dx=-1; dx<=1; ++dx)
//					for(int dy=-1; dy<=1; ++dy)
//						if(dx||dy) mx=max(mx, a[i+dx][j+dy]);
//				if(a[i][j]>mx) ++res;
//			} 
//	if(res!=k) printf("!!!!!!!!!! %d %d %d\n", n, m, k), system("pause");
}
signed main(){
	int T=read();while(T--){
		int n=read(), m=read(), k=read();
		slv(n, m, k);
	}
//	for(int i=1; i<=100; ++i)
//		for(int j=1; j<=100; ++j)
//			for(int k=0; k<=i*j; ++k)
//				slv(i, j, k);
	return 0;
}

H

\[ans=\frac{\sum_{j=0}^{\min(k, m)}C_{k}^j C_{n-k}^{m-j}(k-j)}{\sum_{j=0}^{\min(k, m)}C_{k}^j C_{n-k}^{m-j}} \]

Code
#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int cp=998244353;
inline int read(){
	char ch=getchar();int x=0;
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0', ch=getchar();
	return x;
}
const int N=1e6+6;
int fac[N], inv[N];
inline int C(int x, int y){
	if(x<y||y<0||x<0) return 0;
	return fac[x]*inv[y]%cp*inv[x-y]%cp;
}
inline int ksm(int a, int b=cp-2){
	int res=1;
	while(b){
		if(b&1) res=res*a%cp;
		b>>=1, a=a*a%cp;
	}return res;
}
signed main(){
	int n=read(), k=read(), m=read(), q=read();
	for(int i=fac[0]=1; i<=n; ++i) fac[i]=fac[i-1]*i%cp;
	inv[n]=ksm(fac[n]);
	for(int i=n; i>=1; --i) inv[i-1]=inv[i]*i%cp;
	int ans=0, fm=0;//printf("%lld\n", C(5, 3));
	for(int j=0; j<=min(k, m); ++j){
		int res=C(k, j)*C(n-k, m-j)%cp;
		if(res!=0) fm=(fm+res)%cp;
		ans=(ans+res*(k-j)%cp)%cp;
	}
//	printf("%lld %lld\n", ans, fm);
	ans=ans*ksm(fm)%cp;
	printf("%lld\n", ans);
	return 0;
}

M

容易写出 \(O(n^2)\) DP:

\[f_{i, j}=\max\{f_{k, j-1}+\gcd(a_{k+1}, \dots a_i)\} \]

考虑答案 \(\frac{f_{n, j}}{j}\),二分之,变成分段的惩罚

\[\frac{f_{n, j}}{j}\ge mid\to f_{n, j}-j\times mid\ge 0 \]

\[g_{i}=-mid+\max\{g_{k}+\gcd(a_{k+1}, \dots a_i)\} \]

注意到 \(\gcd(a_{k+1}, \dots a_i)\) 变化时,值至少 \(/2\),对于一个 \(i\) 最多有 \(O(\log v)\) 段不同的 \(\gcd(a_{k+1}, \dots a_i)\),每段若能求得最小的 \(g\) 即可快速 \(O(\log v)\) 转移。

那么考虑从 \(i-1\) 的后缀 \(\gcd\) 变为 \(i\) 的后缀 \(\gcd\),每个对 \(a_i\) 取完 \(\gcd\) 后合并连续相同的,区间 \(\min g\) 也能在此时实现。

直接模拟的话 check\(O(n\log^2 v)\) 的,多的一个 $\log $ 瓶颈在于每次对 \(a_i\)\(\gcd\),注意到 \(mid\) 不影响这些 \(\gcd\),提前预处理好即可。

Code
#include <bits/stdc++.h>
#define vi vector<int>
#define pb push_back
#define mp make_pair
#define st first
#define nd second
using namespace std;
typedef long long ll;
typedef pair <int, int> Pii;
const int INF=0x3f3f3f3f;
const int cp=998244353;
inline int mod(int x){return x+(x<0?cp:0)-(x>=cp?cp:0);}
inline void plust(int &x, int y){x=mod(x+y);return ;}
inline void minut(int &x, int y){x=mod(x-y);return ;}
inline int read(){
	char ch=getchar();int x=0, f=1;
	while(!isdigit(ch)){if(ch=='-') f=-1; ch=getchar();}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(int x){
    if(x<0) putchar('-'), x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
inline int ksm(int a, int b=cp-2){
	int ret=1;
	for(; b; b>>=1, a=1ll*a*a%cp)
		if(b&1) ret=1ll*ret*a%cp;
	return ret;
}
const int N=2e5+5;
const double inf=-1e6;
int gcd(int x, int y){return y?gcd(y, x%y):x;}
vector < pair<int, Pii> >F[N]; 
vector < pair<int, double> > G[N];
double f[N];int n, a[N];
bool check(double k){
	G[1].clear();G[1].pb({a[1], 0});
	f[1]=a[1]-k;
	for(int i=2; i<=n; ++i){
		int p=0;
		for(auto v:F[i]){
			int l=v.nd.st, r=v.nd.nd, lp=min(r, (int)F[i-1].size()-1);
			double mxf=inf;
			for(int j=l; j<=lp; ++j) mxf=max(mxf, G[i-1][j].nd);
			if(lp!=r) mxf=max(mxf, f[i-1]);
			G[i][p++]={v.st, mxf};
		}
		f[i]=inf;
		for(auto v:G[i]) f[i]=max(f[i], v.nd+v.st-k);
	}
	return f[n]>=0;
}
void slv(){
	n=read();a[1]=read();
	F[1].clear();
	F[1].pb({a[1], {0, 0}});
	for(int i=2; i<=n; ++i){
		a[i]=read();vi tmp;F[i].clear();
		for(auto v:F[i-1]) tmp.pb(gcd(a[i], v.st));tmp.pb(a[i]);
		int len=tmp.size();
		for(int l=0, r; l<len; l=r+1){
			r=l;while(r+1<len&&tmp[r+1]==tmp[l]) ++r;
			F[i].pb({tmp[l], {l, r}});
		}
		G[i].resize(F[i].size());
		// for(auto v:F[i]) printf("%d ", v.st);puts("");
	}
	double l=0, r=-inf, ans=0;
	// check(5.000);
	for(int i=0; i<=50; ++i){
		double mid=(l+r)/2.0;
		if(check(mid)) l=mid, ans=mid;
		else r=mid;
	}printf("%.12lf\n", ans);
}
signed main(){
	int T=read();while(T--) slv();
	return 0;
}

D

设源点 \(S\),汇点 \(T\),物品点叫 \(s_i\),工厂点叫 \(t_i\)

那么 \(S\) 往每个 \(s_i\) 连边,流量 \(a_i\),费用为 \(0\)

每个 \(t_i\)\(T\) 连边,流量 \(b_i\),费用为 \(0\)

每个 \(s_i\) 往每个 \(t_j\) 连边,流量 \(1\),费用为 \(w_{i, j}\)

对建立的图跑有最大费用最大流,使用Primal-Dual 原始对偶算法即可做到 \(O(n^3+(\sum b_i)n^2\log (n^4))\)

Code
#include <bits/stdc++.h>
using namespace std;
// Primal-Dual min-cost max-flow (using potentials + Dijkstra).
// We maximize total value by sending required flow with costs = -w.
struct Edge {
    int to, rev, cap;
    long long cost;
};
struct MinCostMaxFlow {
    int n, src, sink;
    vector<vector<Edge>> g;
    vector<long long> pot;   // potentials to ensure non-negative reduced costs
    vector<long long> dist;  // shortest distances in reduced graph
    vector<pair<int, int>> pre; // previous node and edge index on shortest path
    explicit MinCostMaxFlow(int n_) : n(n_), g(n_), pot(n_), dist(n_), pre(n_) {}
    void add_edge(int u, int v, int cap, long long cost) {
        Edge a{v, (int)g[v].size(), cap, cost};
        Edge b{u, (int)g[u].size(), 0, -cost};
        g[u].push_back(a);
        g[v].push_back(b);
    }
    // SPFA to get initial potentials even with negative costs.
    void init_potentials(int s) {
        const long long INF = (1LL << 60);
        fill(pot.begin(), pot.end(), INF);
        vector<int> inq(n, 0);
        queue<int> q;
        pot[s] = 0;
        q.push(s);
        inq[s] = 1;
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            inq[u] = 0;
            for (const auto &e : g[u]) {
                if (e.cap && pot[u] + e.cost < pot[e.to]) {
                    pot[e.to] = pot[u] + e.cost;
                    if (!inq[e.to]) {
                        inq[e.to] = 1;
                        q.push(e.to);
                    }
                }
            }
        }
        for (auto &v : pot) if (v == INF) v = 0; // unreachable nodes keep zero potential
    }
    pair<int, long long> max_cost_flow(int s, int t, int need) {
        src = s;
        sink = t;
        const long long INF = (1LL << 60);
        init_potentials(s);
        int flow = 0;
        long long cost = 0;
        while (flow < need) {
            fill(dist.begin(), dist.end(), INF);
            vector<int> inq(n, 0);
            priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<pair<long long, int>>> pq;
            dist[s] = 0;
            pq.push({0, s});
            while (!pq.empty()) {
                auto [d, u] = pq.top();
                pq.pop();
                if (d != dist[u]) continue;
                for (int i = 0; i < (int)g[u].size(); ++i) {
                    const auto &e = g[u][i];
                    if (!e.cap) continue;
                    long long w = e.cost + pot[u] - pot[e.to];
                    if (dist[u] + w < dist[e.to]) {
                        dist[e.to] = dist[u] + w;
                        pre[e.to] = {u, i};
                        pq.push({dist[e.to], e.to});
                    }
                }
            }
            if (dist[t] == INF) break; // no more augmenting path
            for (int v = 0; v < n; ++v) if (dist[v] < INF) pot[v] += dist[v];
            // augment 1 unit along shortest path (all item-factory edges have cap 1)
            int add = need - flow;
            int v = t;
            while (v != s) {
                auto [u, idx] = pre[v];
                add = min(add, g[u][idx].cap);
                v = u;
            }
            v = t;
            while (v != s) {
                auto [u, idx] = pre[v];
                Edge &e = g[u][idx];
                Edge &rev = g[v][e.rev];
                e.cap -= add;
                rev.cap += add;
                cost += 1LL * add * e.cost;
                v = u;
            }
            flow += add;
        }
        return {flow, cost};
    }
};
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    if (!(cin >> T)) return 0;
    while (T--) {
        int n, m;
        cin >> n >> m;
        vector<int> a(n), b(m);
        long long need = 0;
        for (int i = 0; i < n; ++i) {
            cin >> a[i];
            need += a[i];
        }
        long long cap_sum = 0;
        for (int j = 0; j < m; ++j) {
            cin >> b[j];
            cap_sum += b[j];
        }
        vector<vector<long long>> w(n, vector<long long>(m));
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) cin >> w[i][j];
        }
        if (cap_sum < need) {
            cout << -1 << '\n';
            continue;
        }
        int N = 1 + n + m + 1; // S + items + factories + T
        int S = 0;
        int Tt = N - 1;
        MinCostMaxFlow mcmf(N);
        for (int i = 0; i < n; ++i) mcmf.add_edge(S, 1 + i, a[i], 0);
        for (int j = 0; j < m; ++j) mcmf.add_edge(1 + n + j, Tt, b[j], 0);
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                // capacity 1, cost = -w to maximize gain
                mcmf.add_edge(1 + i, 1 + n + j, 1, -w[i][j]);
            }
        }
        auto [flow, cost] = mcmf.max_cost_flow(S, Tt, (int)need);
        if (flow != need) {
            cout << -1 << '\n';
        } else {
            cout << -cost << '\n';
        }
    }
    return 0;
}

从小到大没写过流,代码拿AI跑的,擦线过时限了

然鹅将原始对偶的堆版 dij 换成 \(O(V^2+E)\) 的即可做到少 \(\log\)

Code
#include <bits/stdc++.h>
using namespace std;
// Primal-Dual min-cost max-flow using adjacency matrices and O(V^2+E) Dijkstra (no heap).
struct MinCostMaxFlow {
    int n;
    vector<vector<int>> cap;          // residual capacities
    vector<vector<long long>> cost;   // edge costs
    vector<long long> pot;            // potentials
    vector<long long> dist;           // distances in reduced graph
    vector<int> pre;                  // previous node on shortest path
    explicit MinCostMaxFlow(int n_) : n(n_), cap(n_, vector<int>(n_, 0)), cost(n_, vector<long long>(n_, 0)), pot(n_), dist(n_), pre(n_) {}
    void add_edge(int u, int v, int c, long long w) {
        cap[u][v] = c;
        cap[v][u] = 0;
        cost[u][v] = w;
        cost[v][u] = -w;
    }
    // SPFA to initialize potentials when negative edges exist.
    void init_potentials(int s) {
        const long long INF = (1LL << 60);
        fill(pot.begin(), pot.end(), INF);
        vector<char> inq(n, 0);
        queue<int> q;
        pot[s] = 0;
        q.push(s);
        inq[s] = 1;
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            inq[u] = 0;
            for (int v = 0; v < n; ++v) {
                if (cap[u][v] == 0) continue;
                long long nd = pot[u] + cost[u][v];
                if (nd < pot[v]) {
                    pot[v] = nd;
                    if (!inq[v]) { inq[v] = 1; q.push(v); }
                }
            }
        }
        for (auto &v : pot) if (v == INF) v = 0; // unreachable nodes keep zero potential
    }
    pair<int, long long> max_cost_flow(int s, int t, int need) {
        const long long INF = (1LL << 60);
        init_potentials(s);
        int flow = 0;
        long long cost_sum = 0;
        while (flow < need) {
            fill(dist.begin(), dist.end(), INF);
            vector<char> vis(n, 0);
            dist[s] = 0;
            for (int iter = 0; iter < n; ++iter) {
                int u = -1;
                long long best = INF;
                for (int v = 0; v < n; ++v) if (!vis[v] && dist[v] < best) { best = dist[v]; u = v; }
                if (u == -1 || best == INF) break;
                vis[u] = 1;
                for (int v = 0; v < n; ++v) if (cap[u][v] > 0) {
                    long long w = cost[u][v] + pot[u] - pot[v];
                    if (dist[u] + w < dist[v]) {
                        dist[v] = dist[u] + w;
                        pre[v] = u;
                    }
                }
            }
            if (dist[t] == INF) break; // no more augmenting path
            for (int v = 0; v < n; ++v) if (dist[v] < INF) pot[v] += dist[v];
            int add = need - flow;
            int v = t;
            while (v != s) {
                int u = pre[v];
                add = min(add, cap[u][v]);
                v = u;
            }
            v = t;
            while (v != s) {
                int u = pre[v];
                cap[u][v] -= add;
                cap[v][u] += add;
                cost_sum += 1LL * add * cost[u][v];
                v = u;
            }
            flow += add;
        }
        return {flow, cost_sum};
    }
};
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    if (!(cin >> T)) return 0;
    while (T--) {
        int n, m;
        cin >> n >> m;
        vector<int> a(n), b(m);
        long long need = 0;
        for (int i = 0; i < n; ++i) {
            cin >> a[i];
            need += a[i];
        }
        long long cap_sum = 0;
        for (int j = 0; j < m; ++j) {
            cin >> b[j];
            cap_sum += b[j];
        }
        vector<vector<long long>> w(n, vector<long long>(m));
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) cin >> w[i][j];
        }
        if (cap_sum < need) {
            cout << -1 << '\n';
            continue;
        }
        int N = 1 + n + m + 1; // S + items + factories + T
        int S = 0;
        int Tt = N - 1;
        MinCostMaxFlow mcmf(N);
        // S -> s_i (capacity a[i], cost 0)
        for (int i = 0; i < n; ++i) mcmf.add_edge(S, 1 + i, a[i], 0);
        // t_j -> T (capacity b[j], cost 0)
        for (int j = 0; j < m; ++j) mcmf.add_edge(1 + n + j, Tt, b[j], 0);
        // s_i -> t_j (capacity 1, cost = -w[i][j] to maximize value)
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                mcmf.add_edge(1 + i, 1 + n + j, 1, -w[i][j]);
            }
        }
        auto [flow, cost] = mcmf.max_cost_flow(S, Tt, (int)need);
        if (flow != need) {
            cout << -1 << '\n';
        } else {
            cout << -cost << '\n';
        }
    }
    return 0;
}

J

设询问模式二进制串 \(S_i\)\(bit_s\)\(s\)1 的个数,则所求

\[\max_{T} f(T), f(T)=\sum a_{bit_{S_{i}\& T}} \]

容易高维前缀和统计超集和 \(cnt_t=\sum [t \sube S_i]\),考虑按位容斥

我们考虑每个 \(bit_s \le n, s\in S_i\)\(s\),我们希望它能合法贡献到 \(f(T)\),显然 \(s\sube S_i\& T\to s\sube T\),考虑构造子集容斥系数。

对于 \(0=s_0\subset s_1\subset s_2 \subset s_3 \subset \dots s_{m}=S_i\& T\)(其中下标表示 1 的个数,\(m\le bit_T\)),我们不希望 \(s_{1-(m-1)}\) 有贡献,

考虑二项式容斥,待定容斥系数 \(q_i\),我们希望

\[g(S_k\& T)=\sum_{i=0}^{m} \binom{m}{i}q_i=a_m \]

显然有二项式反演的形式,令

\[q_i=\sum_{j=0}^{i} (-1)^{i-j}\binom{i}{j} a_i \]

即可。

实际上,在计算 \(g\) 的过程中,它是分散在 \(cnt_t\) 里的,于是对 \(\phi(t)=cnt_t\times q_{bit_t}\) 做高维前缀和统计子集和得到 \(f(T)\),枚举所有情况比较即可

Code
#include <bits/stdc++.h>
#define int long long
#define int long long
#define vi vector<int>
#define pb push_back
#define mp make_pair
#define st first
#define nd second
using namespace std;
typedef long long ll;
typedef pair <int, int> Pii;
const int INF=0x3f3f3f3f;
const int cp=998244353;
inline int mod(int x){return x+(x<0?cp:0)-(x>=cp?cp:0);}
inline void plust(int &x, int y){x=mod(x+y);return ;}
inline void minut(int &x, int y){x=mod(x-y);return ;}
inline int read(){
	char ch=getchar();int x=0, f=1;
	while(!isdigit(ch)){if(ch=='-') f=-1; ch=getchar();}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(int x){
    if(x<0) putchar('-'), x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
inline int ksm(int a, int b=cp-2){
	int ret=1;
	for(; b; b>>=1, a=1ll*a*a%cp)
		if(b&1) ret=1ll*ret*a%cp;
	return ret;
}
signed main(){
	int C[24][24]={};
	for(int i=0; i<=23; ++i)
		for(int j=C[i][0]=C[i][i]=1; j<i; ++j) C[i][j]=C[i-1][j-1]+C[i-1][j];
	int n=read(), m=read(), k=read(), M=1<<m;
	vi a(n+1, 0);for(int i=1; i<=n; ++i) a[i]=read();
	vi cnt(M, 0);
	for(int i=1; i<=k; ++i){
		int s=0;for(int j=1; j<=n; ++j) s|=1<<(read()-1);
		++cnt[s];
	}
	for(int i=0, t=1; i<m; ++i, t<<=1)
		for(int s=0; s<M; ++s)
			if(!(s&t)) cnt[s]+=cnt[s|t];
	vi f(n+1, 0), g(M, 0);
	for(int i=0; i<=n; ++i)
		for(int j=0; j<=i; ++j)
			f[i]+=(((i-j)&1)?-1ll:1ll)*C[i][j]*a[j];
	for(int s=0; s<M; ++s){
		int bp=__builtin_popcount(static_cast<unsigned>(s));
		if(bp<=n) g[s]=f[bp]*cnt[s];
	}
	for(int i=0, t=1; i<m; ++i, t<<=1)
		for(int s=0; s<M; ++s)
			if(s&t) g[s]+=g[s^t];
	int ans=LLONG_MAX, S=0;
	for(int s=0; s<M; ++s){
		if(__builtin_popcount(static_cast<unsigned>(s))!=n) continue;
		int c=g[s];if(c<ans||(c==ans&&s<S)) ans=c, S=s;
	}
	printf("%lld\n", ans);
	for(int i=m-1, t=1<<m-1; i>=0; --i, t>>=1) if(S&t) printf("%lld ", i+1);
	return 0;
}
posted @ 2025-12-20 23:36  127_127_127  阅读(13)  评论(0)    收藏  举报