C:HSI
期望模型,不想说。
1 #include<cstdio> 2 using namespace std; 3 typedef long long ll; 4 int main() 5 { 6 int n,m; 7 scanf("%d%d",&n,&m); 8 printf("%lld ",(ll)((n-m)*100+m*1900)*(1<<m)); 9 return 0; 10 }
D:ABS
f[i][j]表示第i轮,X在i,Y在j,玩到最后的差绝对值。下一次由Y先手,取所有可以的转移中的最小值。
g[i][j]表示第i轮,Y在i,X在j,玩到最后的差绝对值。下一次由X先手,取所有可以的转移中的最大值。
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int N=2005; 5 int n,z,w,a[N],ans,Mn_g[N],Mx_f[N],f[N][N],g[N][N]; 6 int main() 7 { 8 scanf("%d%d%d",&n,&z,&w); 9 for (int i=1;i<=n;i++) scanf("%d",&a[i]); 10 a[0]=w; 11 for (int i=0;i<n;i++) f[n][i]=g[n][i]=Mx_f[i]=Mn_g[i]=abs(a[n]-a[i]); 12 for (int i=n-1;i>=0;i--) 13 for (int j=0;j<i;j++) 14 { 15 if (i) f[i][j]=Mn_g[i],Mx_f[j]=max(Mx_f[j],f[i][j]); 16 if (j) g[i][j]=Mx_f[i],Mn_g[j]=min(Mn_g[j],g[i][j]); 17 } 18 for (int i=1;i<=n;i++) ans=max(ans,f[i][0]); 19 printf("%d ",ans); 20 return 0; 21 }
E:MUL
题意:n个宝石,有价值ai,可以为负。你可以打碎若干宝石和其所有标号有倍数关系的宝石。问剩下的宝石最大价值和?
1 #include<cstdio> 2 #include<algorithm> 3 #include<queue> 4 #include<cstring> 5 using namespace std; 6 typedef long long ll; 7 const int N=105; 8 const ll inf=1ll<<60; 9 int cnt=1,head[N],Head[N],n,dis[N],S,T,x; 10 ll ans,tmp; 11 queue<int> q; 12 struct node{int to,next;ll w;}num[N*N*2]; 13 void add(int x,int y,ll w) 14 {num[++cnt].to=y;num[cnt].next=head[x];num[cnt].w=w;head[x]=cnt; 15 num[++cnt].to=x;num[cnt].next=head[y];num[cnt].w=0;head[y]=cnt;} 16 int bfs() 17 { 18 memset(dis,0,sizeof(dis));dis[S]=1; 19 q.push(S); 20 while (!q.empty()) 21 { 22 int now=q.front();q.pop(); 23 for (int i=head[now];i;i=num[i].next) 24 if (num[i].w&&!dis[num[i].to]) 25 q.push(num[i].to),dis[num[i].to]=dis[now]+1; 26 } 27 return dis[T]; 28 } 29 ll dfs(int x,ll mm) 30 { 31 ll tmp=mm; 32 if (x==T) return mm; 33 for (int &i=Head[x];i&&tmp;i=num[i].next) 34 if (dis[num[i].to]==dis[x]+1) 35 { 36 ll t=dfs(num[i].to,min(num[i].w,tmp)); 37 tmp-=t;num[i].w-=t;num[i^1].w+=t; 38 } 39 return mm-tmp; 40 } 41 void dinic() 42 { 43 while (bfs()) 44 { 45 memcpy(Head,head,sizeof(head)); 46 while (tmp=dfs(S,inf)) ans-=tmp; 47 } 48 } 49 int main() 50 { 51 scanf("%d",&n);S=n+1;T=S+1; 52 for (int i=1;i<=n;i++) 53 { 54 scanf("%d",&x); 55 if (x<0) add(S,i,-x);else add(i,T,x),ans+=x; 56 } 57 for (int i=1;i<=n;i++) 58 for (int j=i+i;j<=n;j+=i) add(i,j,inf); 59 dinic(); 60 printf("%lld ",ans); 61 return 0; 62 }
最小割建模(又忘记套路了真是该打)
S部设为不选,T部为选。权值为负的点向S连容量为-w的边,权值为正的点向T连容量为w的边。这里的容量相当于是割掉这条边的代价。
对于所有有倍数关系的点,小的向大的连inf的边。ans一开始为所有正权边的权值和。
比如对于i和2i,S-i,i-2i,2i-T。那么割掉i-2i,就是不选i而选2i,不可能。割掉S-i,就是都保留,那么ans-=w[i]。割掉2i-T,就是都不保留,那么ans-=w[2i]。所以最大权值也就是一个最小割的问题啦。
建模方法:分为选和不选两部分点,把边的容量设为代价,通过正负的约束来使得要求的是最小割,ans一开始统计全部保留和T点(保留点)连边的价值和。
F:NRE
题意:给你若干个区间,你可以从中选取部分来使得一整个区间变成1。
初始序列为A,问进行一些操作后,和B的最少不同位?
n<=20W。
1 #include<bits/stdc++.h> 2 #define mid ((l+r)>>1) 3 using namespace std; 4 const int inf=0x3f3f3f3f; 5 const int N=200005; 6 int T[N<<2],tag[N<<2],n,a[N],sum0[N],sum1[N],Q,head; 7 struct node{int l,r;}q[N]; 8 bool operator < (const node &A,const node &B) 9 {return A.l<B.l||A.l==B.l&&A.r<B.r;} 10 void down(int k) 11 { 12 if (tag[k]) 13 { 14 tag[k<<1]+=tag[k];tag[k<<1|1]+=tag[k]; 15 T[k<<1]+=tag[k];T[k<<1|1]+=tag[k]; 16 tag[k]=0; 17 } 18 } 19 void ins(int k,int l,int r,int x,int y) 20 { 21 if (l==r) {T[k]=min(T[k],y);return;} 22 down(k); 23 if (x<=mid) ins(k<<1,l,mid,x,y); 24 else ins(k<<1|1,mid+1,r,x,y); 25 T[k]=min(T[k<<1],T[k<<1|1]); 26 } 27 void add(int k,int l,int r,int L,int R,int x) 28 { 29 if (L<=l&&r<=R) {T[k]+=x;tag[k]+=x;return;} 30 down(k); 31 if (L<=mid) add(k<<1,l,mid,L,R,x); 32 if (R>mid) add(k<<1|1,mid+1,r,L,R,x); 33 T[k]=min(T[k<<1],T[k<<1|1]); 34 } 35 int qry(int k,int l,int r,int L,int R) 36 { 37 if (L<=l&&r<=R) return T[k]; 38 down(k);int ans=inf; 39 if (L<=mid) ans=min(ans,qry(k<<1,l,mid,L,R)); 40 if (R>mid) ans=min(ans,qry(k<<1|1,mid+1,r,L,R)); 41 return ans; 42 } 43 int main() 44 { 45 scanf("%d",&n); 46 for (int i=1;i<=n;i++) 47 { 48 scanf("%d",&a[i]);sum0[i]=sum0[i-1];sum1[i]=sum1[i-1]; 49 if (!a[i]) sum0[i]++;else sum1[i]++; 50 } 51 scanf("%d",&Q); 52 for (int i=1;i<=Q;i++) scanf("%d%d",&q[i].l,&q[i].r); 53 sort(q+1,q+Q+1); 54 head=1;memset(T,inf,sizeof(T)); 55 ins(1,0,n,0,0); 56 for (int i=1;i<=n;i++) 57 { 58 while (head<=Q&&q[head].l==i) ins(1,0,n,q[head].r,qry(1,0,n,0,q[head].r)),head++; 59 if (a[i]) add(1,0,n,0,i-1,1);else add(1,0,n,0,i-1,-1); 60 } 61 printf("%d ",qry(1,0,n,0,n)+sum0[n]); 62 return 0; 63 }
线段树优化dp
我们要求的是(0,1)+(1,0)的数量,也就是(0,1)+(0/1,0)-(0,0)。那么(0/1,0)是固定的,即要使得(0,1)-(0,0)最小。1的覆盖也就是不计算一些位置的贡献。
哇这个dp思路超级巧妙。dp[i][j]表示前i个数确定,当前最后一个1在j的最小(0,1)-(0,0)。
从小到大枚举i,对于一条左端点在i的线段[i,r],dp[i][r]=min(dp[i-1][k]),k<=r。(因为i之后都是1,相当于只有前i-1位确定即可。而且如果k>r,那么之前那条线段一定包含[i,r],dp[i][r]的更新跟它就没有什么关系了,况且根据定义最后一个1就出界了),其他没有更改的和dp[i-1]一样。
此时落点>=i的线段一定覆盖了i点(因为线段的左端点<=i),因而我们需要对于所有dp[i][0~i-1]都更改为前i个数确定的状态,由第i个数的0/1决定-1/+1。区间最小值和区间加用线段树实现。时间复杂度O(nlogn)。