A:如果C在D左侧,显然先让B到达终点再让A走即可,否则先判断一下A是否可以在某处超过B。也就是先判断一下起点与终点之间是否有连续的障碍,若有则无解;然后若C在D左侧输出Yes,否则判断B和D之间是否有存在某个空地其左右均为空地,若有则输出Yes,否则输出No。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1000000010
#define N 200010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int n,a,b,c,d;
char s[N];
signed main()
{
n=read(),a=read(),b=read(),c=read(),d=read();
scanf("%s",s+1);
for (int i=1;i<n;i++) if (s[i]=='#'&&s[i+1]=='#'&&(a<=i&&c>i||b<=i&&d>i)) {cout<<"No";return 0;}
if (c>d)
{
int pos=-1;
for (int i=b;i<=d;i++) if (s[i-1]=='.'&&s[i]=='.'&&s[i+1]=='.') {pos=i;break;}
if (pos==-1) {cout<<"No";return 0;}
else cout<<"Yes";
}
else cout<<"Yes";
return 0;
//NOTICE LONG LONG!!!!!
}

  B:考虑每个BC能与哪些A一起做出贡献,显然这样的A应在该BC前面,且它们之间只有A和连续出现的BC出现。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1000000010
#define N 200010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int n;
char s[N];
signed main()
{
int cnt=0;ll ans=0;
scanf("%s",s+1);n=strlen(s+1);
for (int i=1;i<=n;i++)
if (s[i]=='A') cnt++;
else if (s[i]=='B')
{
if (s[i+1]=='C') i++,ans+=cnt;
else cnt=0;
}
else cnt=0;
cout<<ans;
return 0;
//NOTICE LONG LONG!!!!!
}

  C:首先显然的是每门课权重要么取max要么取min,其中学的比对方多时取max,比对方少时取min。

  然后注意到最多只有一门课不学满,其它课要么学满要么不学,因为假设最优解中有超过一门课不学满,显然将其中权重低的改为权重高的不会使答案更劣。

  假设固定了某一门课不学满及学多少,对于剩余的课,将学满获得的收益减去不学获得的减益定义为价值,按价值从高到低依次选择学满即可。

  这个过程可以改为先按价值排序,求出假设要么不学要么学满,最优要学满哪些课程才能满足条件,然后原问题的最优解显然是从这些学满的课程中删去一个再学习某个课程。

  如果删去的不是被选中的课程中价值最低的,最优方案一定不会是选择其他课程,因为显然不优;而如果是价值最低的,可以枚举所有其他课程进行选择。先二分一下答案就好了。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1000000010
#define N 100010
#define int long long
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int n,m,pos;
ll ans,s[N],tot;
struct data
{
int x,l,r;
bool operator <(const data&a) const
{
return 1ll*(m-x)*r+1ll*x*l>1ll*(m-a.x)*a.r+1ll*a.x*a.l;
}
}a[N];
ll mxval(int i){return 1ll*(m-a[i].x)*a[i].r;}
ll val(int i,int k){if (k<a[i].x) return 1ll*(k-a[i].x)*a[i].l;else return 1ll*(k-a[i].x)*a[i].r;}
bool check(int k,ll tot)
{
for (int i=1;i<pos;i++)
if (tot-mxval(i)+val(i,k)>=0) return 1;
tot-=mxval(pos);tot+=val(pos,0);
for (int i=pos;i<=n;i++)
if (tot-val(i,0)+val(i,k)>=0) return 1;
return 0;
}
signed main()
{
n=read(),m=read();
for (int i=1;i<=n;i++) a[i].x=read(),a[i].l=read(),a[i].r=read();
sort(a+1,a+n+1);
for (int i=n;i>=1;i--) s[i]=s[i+1]-val(i,0);
tot=0;pos=0;
for (int i=1;i<=n;i++)
{
tot+=mxval(i);
if (tot>=s[i+1]) {pos=i;break;}
}
if (pos==0) {cout<<0;return 0;}
tot-=s[pos+1];
int l=0,r=m;
while (l<=r)
{
int mid=l+r>>1;
if (check(mid,tot)) ans=mid,r=mid-1;
else l=mid+1;
}
cout<<ans+1ll*(pos-1)*m;
return 0;
//NOTICE LONG LONG!!!!!
}

  D:直接暴力跑费用流边的数量过多。注意到|x1-x2|+|y1-y2|=max{(x1+y1)+(-x2-y2),(x1-y1)+(-x2+y2),(-x1+y1)+(x2-y2),(-x1-y1)+(x2+y2)},于是可以看成是给每个点在四种颜色中选择一种颜色,不同点的不同颜色价值不同,要求两组点中四种颜色出现次数均相同,最大化价值和。这个东西可以费用流,即两排点分别对应两组点,中间四个点表示四种颜色,源汇对应向两组点连边,容量为该坐标对应的点的数量,费用为0;两组点分别向四种颜色连边,容量inf,费用为该点取该颜色时的价值的相反数。这样边数就是O(n)的,跑费用流即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1000000010
#define N 2010
#define S 0
#define T 2001
#define red(x) (x)
#define blue(x) (n+(x))
#define id(x) (2002+(x))
#define int long long
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int n,p[N],d[N],q[N],pre[N],t=-1,ans;
bool flag[N];
struct data{int x,y,c,v[4];
}a[N],b[N];
struct data2{int to,nxt,cap,flow,cost;
}edge[N<<5];
void addedge(int x,int y,int z,int cost)
{
t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].cap=z,edge[t].flow=0,edge[t].cost=cost,p[x]=t;
t++;edge[t].to=x,edge[t].nxt=p[y],edge[t].cap=0,edge[t].flow=0,edge[t].cost=-cost,p[y]=t;
}
int inc(int &x){x++;if (x>2007) x-=2007;return x;}
bool spfa()
{
memset(d,60,sizeof(d));d[S]=0;
memset(flag,0,sizeof(flag));
int head=0,tail=1;q[1]=S;
do
{
int x=q[inc(head)];flag[x]=0;
for (int i=p[x];~i;i=edge[i].nxt)
if (edge[i].flow<edge[i].cap&&d[x]+edge[i].cost<d[edge[i].to])
{
d[edge[i].to]=d[x]+edge[i].cost;
pre[edge[i].to]=i;
if (!flag[edge[i].to]) q[inc(tail)]=edge[i].to,flag[edge[i].to]=1;
}
}while (head!=tail);
return d[T]<10000000000000000ll;
}
void ekspfa()
{
while (spfa())
{
int v=n;
for (int i=T;i!=S;i=edge[pre[i]^1].to)
v=min(v,edge[pre[i]].cap-edge[pre[i]].flow);
for (int i=T;i!=S;i=edge[pre[i]^1].to)
edge[pre[i]].flow+=v,edge[pre[i]^1].flow-=v,ans+=v*edge[pre[i]].cost;
}
}
signed main()
{
n=read();memset(p,255,sizeof(p));
for (int i=1;i<=n;i++)
{
a[i].x=read(),a[i].y=read(),a[i].c=read();
a[i].v[0]=a[i].x+a[i].y;
a[i].v[1]=a[i].x-a[i].y;
a[i].v[2]=-a[i].x+a[i].y;
a[i].v[3]=-a[i].x-a[i].y;
}
for (int i=1;i<=n;i++)
{
b[i].x=read(),b[i].y=read(),b[i].c=read();
b[i].v[0]=-b[i].x-b[i].y;
b[i].v[1]=-b[i].x+b[i].y;
b[i].v[2]=b[i].x-b[i].y;
b[i].v[3]=b[i].x+b[i].y;
}
for (int i=1;i<=n;i++)
{
addedge(S,red(i),a[i].c,0),addedge(blue(i),T,b[i].c,0);
for (int j=0;j<4;j++)
addedge(red(i),id(j),a[i].c,-a[i].v[j]),addedge(id(j),blue(i),b[i].c,-b[i].v[j]);
}
ekspfa();
cout<<-ans;
return 0;
//NOTICE LONG LONG!!!!!
}

  E:枚举一个根,然后O(n)check。显然将两个点一个往上拉一个往下拉没有任何意义,所以如果有解答案就为所有棋子的深度之和/2。于是只要判断是否有解。

  同样显然的一点是对于根来说可以在其各儿子子树内部操作完后再进行子树间的操作,这样不会造成负面影响。于是可以划分成子问题了。设f[i]为i子树内部移动后深度之和的最小值,d[i]为i子树中所有棋子的深度之和,显然内部操作过程中f[i]~d[i]之间所有与d[i]奇偶性相同的深度之和都能被取到。f[root]=0时即有解。

  考虑转移,先将f[son]和d[son]变为以i为根情况下的值,然后相当于要对每个儿子决定一个f[]~d[]之间变量x[](可以和d[]奇偶性不同,因为子树间可以相互匹配,不考虑奇偶性产生的误差至多为1,而实际上这不造成影响),最小化max{x[son]}*2-Σx[son]。当然这个值还要对d[i]%2取max。假设固定了max{x},要最小化该值,显然其他变量的取值均应为min(x,d[])。容易发现max{x}取d[]中的次大值时最优。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1000000010
#define N 2010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int n,p[N],a[N],t,deep[N],size[N],sigmadeep[N],f[N],root,ans=inf;
struct data{int to,nxt;
}edge[N<<1];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
void dfs(int k,int from)
{
size[k]=a[k];sigmadeep[k]=a[k]*deep[k];
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from)
{
deep[edge[i].to]=deep[k]+1;
dfs(edge[i].to,k);
size[k]+=size[edge[i].to];
sigmadeep[k]+=sigmadeep[edge[i].to];
}
}
void work(int k,int from)
{
int son=0;f[k]=0;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from) son++,work(edge[i].to,k);
if (son==0) f[k]=0;
else
{
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from) sigmadeep[edge[i].to]+=size[edge[i].to],f[edge[i].to]+=size[edge[i].to];
if (son==1)
{
int s=0;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from) s=f[edge[i].to];
f[k]=s;
}
else
{
int mx1=-1,mx2=-1;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from)
if (sigmadeep[edge[i].to]>mx1) mx2=mx1,mx1=sigmadeep[edge[i].to];
else mx2=max(mx2,sigmadeep[edge[i].to]);
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from) mx2=max(mx2,f[edge[i].to]);
f[k]=mx2*2;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from) f[k]-=min(mx2,sigmadeep[edge[i].to]);
}
f[k]=max(f[k],sigmadeep[k]&1);
}
}
//若干个f[]~sigmadeep[]之间的变量,最小化max{x}*2-Σx
//枚举maxx 后,后一部分即Σmin(x,sigmadeep)
//x应取次大deep
signed main()
{
n=read();
for (int i=1;i<=n;i++) if (getc()=='0') a[i]=0;else a[i]=1;
for (int i=1;i<n;i++)
{
int x=read(),y=read();
addedge(x,y),addedge(y,x);
}
for (int i=1;i<=n;i++)
{
memset(deep,0,sizeof(deep));
memset(size,0,sizeof(size));
memset(sigmadeep,0,sizeof(sigmadeep));
memset(f,42,sizeof(f));
dfs(i,i);
for (int j=1;j<=n;j++) sigmadeep[j]-=size[j]*deep[j];
if (sigmadeep[i]&1) continue;
work(i,i);
/*for (int j=1;j<=n;j++) cout<<deep[j]<<' ';cout<<endl;
for (int j=1;j<=n;j++) cout<<size[j]<<' ';cout<<endl;
for (int j=1;j<=n;j++) cout<<sigmadeep[j]<<' ';cout<<endl;
for (int j=1;j<=n;j++) cout<<f[j]<<' ';cout<<endl;cout<<endl;*/
if (f[i]==0) ans=min(ans,sigmadeep[i]/2);
}
if (ans==inf) cout<<-1;else cout<<ans;
return 0;
//NOTICE LONG LONG!!!!!
}
//假设确定根
//f[i]将i子树内的点都移到lca的最小深度之和
//

  result:rank 138 rating +56

最新文章

  1. Query Mobile学习笔记
  2. web—第三章XHTML
  3. Linux基础命令总结
  4. leetcode 13
  5. js 数组 var arr=[] 的用法总结
  6. Java之IO转换流
  7. Chapter 21_1 字符串函数
  8. 平时常用的一些java方法,请留意
  9. 基于Vue.js的大型报告页项目实现过程及问题总结(一)
  10. 【MySQL 读书笔记】当我们在执行更新语句的时候我们在做什么
  11. ch03 课下作业——缓冲区溢出漏洞实验
  12. SqlServer 游标逐行更新数据,根据上一行的数据来更新当前行
  13. 20155231 2016-2017-2 《Java程序设计》第6周学习总结
  14. [C++]Linux之Ubuntu下编译C程序出现错误:“ stray ‘\302&#39;或者&#39;\240&#39; in program”的解决方案
  15. rt.jar sun package
  16. ubuntu 下 go 语言调试器 dlv 的安装
  17. 〖Linux〗穿越城墙之后,直接连接国内网站的路由配置
  18. 第3章 Python基础-文件操作&amp;函数 文件操作 练习题
  19. Java面向对象之抽象类
  20. Jvm内存工具

热门文章

  1. python blob操作
  2. Qt Resource System Qt资源体系(qrc rcc)
  3. ADT中创建Android的Activity
  4. kinova environment
  5. 转 How to Find Out Who Is Locking a Table in MySQL
  6. CentOS7下搭建Redis主从复制
  7. 123457123457#0#----com.ppGame.YinYu45--前拼后广--儿童yinyu-pp
  8. 教孩子学编程 Python
  9. POJ 2584 T-Shirt Gumbo 二分图的多重匹配
  10. 【JS新手教程】JS中的split()方法,拆分字符串