码迷,mamicode.com
首页 > 其他好文 > 详细

Atcoder arc085

时间:2018-04-17 23:43:34      阅读:227      评论:0      收藏:0      [点我收藏+]

标签:arc   next   个数   span   i++   绝对值   容量   sum   ini   

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\n",(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\n",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\n",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点(保留点)连边的价值和。

 

D: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\n",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)。

 

Atcoder arc085

标签:arc   next   个数   span   i++   绝对值   容量   sum   ini   

原文地址:https://www.cnblogs.com/Scx117/p/8868755.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!