赛生新
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
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:
考虑答案 \(\frac{f_{n, j}}{j}\),二分之,变成分段的惩罚
注意到 \(\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 的个数,则所求
容易高维前缀和统计超集和 \(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\) 的过程中,它是分散在 \(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;
}

浙公网安备 33010602011771号