题目让我们模拟每次找出最大的正连通块然后全部权值减一。
容易发现优化这个过程就是在最大的正连通块中找到最小值然后删去它。
因为加点维护连通性有并查集可以写比删点好写一万倍。
所以我们倒过来考虑每次加入值最大的点然后合并一下维护一下花费即可。
A C C o d e \mathcal AC \ Code AC Code
#include
#define maxn 100005
#define LL long long
using namespace std;
int n,m,b[maxn],c[maxn],F[maxn],usd[maxn];
int Find(int u){ return !F[u] ? u : F[u] = Find(F[u]); }
vector<int>G[maxn];
bool cmp(const int &u,const int &v){ return b[u] > b[v]; }
int main(){
int T;
for(scanf("%d",&T);T--;){
scanf("%d%d",&n,&m);
LL ans = 0;
for(int i=1;i<=n;i++) scanf("%d",&b[i]),c[i]=i;
for(int i=1;i<=m;i++){
int u,v;scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
sort(c+1,c+1+n,cmp);
for(int i=1;i<=n;i++){
int u = c[i];
usd[u] = 1;
ans += b[u];
for(int j=0;j<G[u].size();j++) if(usd[G[u][j]]){
int y = Find(G[u][j]) , x = Find(u);
if(x ^ y){
F[x] = y;
ans -= b[x];
b[y] = b[x];
}
}
}
printf("%lld\n",ans);
for(int i=1;i<=n;i++) G[i].clear() , F[i] = usd[i] = 0;
}
}
答案等于两个串长之和减去他们的最长公共子序列的两倍。
因为在一个串插入一定不比在另一个串中删除优秀。
预处理出 g i , j g_{i,j} gi,j表示 i i i第一个 j j j字符的位置。
然后就可以 O ( 1 ) O(1) O(1)转移 f i , j f_{i,j} fi,j表示已经考虑了 B B B的前 i i i位有 j j j位被匹配时 A A A串右边最多能扔多少。
A C C o d e \mathcal AC \ Code AC Code
#include
#define maxn 100005
#define maxc 26
using namespace std;
char A[maxn],B[maxc];
int g[maxn][maxc] , f[maxc][maxc] , n , m;
int main(){
int T;
for(scanf("%d",&T);T--;){
scanf("%s%s",A,B+1);
n = strlen(A) , m = strlen(B+1);
for(int i=0;i<=maxc-1;i++) g[n][i] = n;
for(int i=n-1;i>=0;i--){
memcpy(g[i],g[i+1],sizeof g[i]);
g[i][A[i] - 'a'] = i;
}
int q;
scanf("%d",&q);
for(int l,r;q--;){
scanf("%d%d",&l,&r);l--,r--;
memset(f,0x3f,sizeof f);
f[0][0] = l-1;
for(int i=1;i<=m;i++)
for(int j=0;j<=i;j++)
f[i][j] = min(f[i-1][j] , (j && f[i-1][j-1] < r) ? g[f[i-1][j-1] + 1][B[i] - 'a'] : 0x3f3f3f3f) ;
for(int i=m;i>=0;i--) if(f[m][i] <= r){
printf("%d\n",r-l+1+m-2*i);
break;
}
}
}
}
直接哈希即可。
A C C o d e \mathcal AC \ Code AC Code
#include
#define LL long long
#define maxn 2000006
using namespace std;
char cb[1<<16],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<16,stdin),cs==ct)?0:*cs++)
void read(int &res){
char ch;
for(;!isdigit(ch=getc()););
for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}
LL f[maxn];
int n,m,K;
int main(){
int T;
f[0] = f[1] = 1;
for(int i=2;i<maxn;i++) f[i] = f[i-1] + f[i-2];
for(read(T);T--;){
read(n);
LL A = 0 , B = 0 , C = 0;
for(int i=1;i<=n;i++){
int x;read(x);
if(x) A += f[i];
}
read(m);
for(int i=1;i<=m;i++){
int x;read(x);
if(x) B += f[i];
}
C = A * B;
read(K);
for(int i=1;i<=K;i++){
int x;read(x);
if(x) C -= f[i];
}
for(int i=1;i<=K;i++) if( C == f[i]){
printf("%d\n",i);
break;
}
}
}
注意到如果让每个种类都恰好选一个,那么这样搜的话时间复杂度最大是 O ( 3 n 3 ) O(3^{\frac n3}) O(33n)的。
但是出题人准备了10组数据来搞你心态。
随便写点剪枝即可。
注:写剪枝是因为下面的写法没有剪枝是 O ( n 3 n 3 ) O(n3^\frac{n}3) O(n33n)的,要做到 O ( 3 n 3 ) O(3^{\frac n3}) O(33n)需要在选择了同类型的一个后保证之后不会枚举到同类型的数,具体可以看第二份代码。
A C C o d e \mathcal AC \ Code AC Code
#include
#define LL long long
#define cj 2e8
using namespace std;
int n,K;
#define maxn 55
LL mx[maxn];
int t[maxn],a[maxn],b[maxn],c[maxn],d[maxn],vis[maxn],lst[maxn],id[maxn];
LL ans;
void dfs(int u,int A,int B,int C,int D,int hd){
if(u == n+1){
ans = max(ans , 1ll * A * B * C * D);
return;
}
if(1ll * A * B * C * D <= mx[hd] - cj * hd * hd) return;
mx[hd] = max(mx[hd] , 1ll * A * B * C * D);
int p = id[u];
if(vis[t[p]] || lst[t[p]] != p)dfs(u+1,A,B,C,D,hd);
if(!vis[t[p]])
vis[t[p]] = 1 , dfs(u+1,A+a[p],B+b[p],C+c[p],D+d[p],hd+1) , vis[t[p]] = 0;
}
bool cmp(const int &u,const int &v){ return a[u] + b[u] + c[u] + d[u] > a[v] + b[v] + c[v] + d[v]; }
int main(){//freopen("1.in","r",stdin);
int T;
for(scanf("%d",&T);T--;){
scanf("%d%d",&n,&K);
memset(lst,0,sizeof lst);
memset(mx,0,sizeof mx);
ans = 0;
for(int i=1;i<=n;i++){
scanf("%d%d%d%d%d",&t[i],&a[i],&b[i],&c[i],&d[i]);
id[i] = i;
}
sort(id+1,id+1+n,cmp);
for(int i=1;i<=n;i++)
lst[t[id[i]]] = id[i];
dfs(1,100,100,100,100,0);
printf("%lld\n",ans);
}
}
#include
#define LL long long
using namespace std;
int n,K;
#define maxn 55
LL mx[maxn];
int t[maxn] , nxt[maxn],a[maxn],b[maxn],c[maxn],d[maxn],id[maxn];
bool cmp(const int &u,const int &v){ return t[u] < t[v]; }
LL ans;
void dfs(int u,int A,int B,int C,int D,int hd){
if(u == n+1){
ans = max(ans , 1ll * A * B * C * D);
return;
}
if(nxt[u] != u + 1)
dfs(u+1,A,B,C,D,hd);
dfs(nxt[u],A+a[id[u]],B+b[id[u]],C+c[id[u]],D+d[id[u]],hd+1);
}
int main(){
int T;
for(scanf("%d",&T);T--;){
scanf("%d%d",&n,&K);
ans = 0;
for(int i=1;i<=n;i++)
scanf("%d%d%d%d%d",&t[i],&a[i],&b[i],&c[i],&d[i]) , id[i] = i;
sort(id+1,id+1+n,cmp);
int pr = n+1;
for(int i=n;i>=1;i--){
if(t[id[i]] != t[id[i+1]]) pr = i + 1;
nxt[i] = pr;
}
dfs(1,100,100,100,100,0);
printf("%lld\n",ans);
}
}
这,有点暴力、、。
对于每个二次函数找离它极值点最近的 n n n个点,那么可以发现这个工人一定配对在这 n n n个点中,
把这些 n 2 n^2 n2个点提出来跑最小费用流即可。
A C C o d e \mathcal AC \ Code AC Code
#include
#define maxn 10005
#define maxm 5000006
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
#define inf 0x3f3f3f3f
using namespace std;
int n,m;
int info[maxn],Prev[maxm],to[maxm],cap[maxm],cnt_e=1;
LL cst[maxm];
void Node(int u,int v,int c,LL ct){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cap[cnt_e]=c,cst[cnt_e]=ct; }
void Line(int u,int v,int c,LL ct){ Node(u,v,c,ct) , Node(v,u,0,-ct); }
LL a[maxn],b[maxn],c[maxn];
int sb[maxn],S,T,pr[maxn];
LL h[maxn];
void SPFA(){
static int inq[maxn];
static queue<int>q;
memset(h,0x3f,(sizeof h[0]) * (T+1));
h[T] = 0;
q.push(T);
for(int u;!q.empty();){
u = q.front() , q.pop();
inq[u] = 0;
for(int i=info[u],v;i;i=Prev[i]) if(cap[i^1] && h[v=to[i]] > h[u] + cst[i^1])
pr[v] = i^1 , h[v] = h[u] + cst[i^1] , (!inq[v]) && (q.push(v),0);
}
assert(h[S] != h[0]);
}
int main(){
int cas;
for(scanf("%d",&cas);cas--;){
scanf("%d%d",&n,&m);
rep(i,1,n){
scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
LL t = max(1ll,min(b[i] / (-2 * a[i]) , m * 1ll));
rep(j,max(1ll,t-n+1),min(m*1ll,t+n-1))
sb[++sb[0]] = j;
}
sort(sb+1,sb+1+sb[0]);
sb[0] = unique(sb+1,sb+1+sb[0]) - sb - 1;
rep(i,1,n){
LL t = max(1ll,min(b[i] / (-2 * a[i]) , m * 1ll));
rep(p,max(1ll,t-n+1),min(m*1ll,t+n-1)){
int j = lower_bound(sb+1,sb+sb[0]+1,p) - sb;
Line(i,j+n,1,a[i]*sb[j]*sb[j]+b[i]*sb[j]+c[i]);
}
}
S = n + sb[0] + 1, T = S + 1;
rep(i,1,n) Line(S,i,1,0);
rep(i,1,sb[0]) Line(i+n,T,1,0);
LL ans = 0;
rep(i,1,n){
SPFA();
ans += h[S];
printf("%lld%c",ans," \n"[i==n]);
for(int u=S;u!=T;u=to[pr[u]])
cap[pr[u]] -- , cap[pr[u] ^ 1] ++;
}
rep(i,1,T) info[i] = 0; cnt_e = 1;
sb[0] = 0;
}
}