AtCoder Grand Contest 034

  A:若是C在D左側,顯然先讓B到達終點再讓A走便可,不然先判斷一下A是否能夠在某處超過B。也就是先判斷一下起點與終點之間是否有連續的障礙,如有則無解;而後若C在D左側輸出Yes,不然判斷B和D之間是否有存在某個空地其左右均爲空地,如有則輸出Yes,不然輸出No。c++

#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。spa

  而後注意到最多隻有一門課不學滿,其它課要麼學滿要麼不學,由於假設最優解中有超過一門課不學滿,顯然將其中權重低的改成權重高的不會使答案更劣。blog

  假設固定了某一門課不學滿及學多少,對於剩餘的課,將學滿得到的收益減去不學得到的減益定義爲價值,按價值從高到低依次選擇學滿便可。排序

  這個過程能夠改成先按價值排序,求出假設要麼不學要麼學滿,最優要學滿哪些課程才能知足條件,而後原問題的最優解顯然是從這些學滿的課程中刪去一個再學習某個課程。get

  若是刪去的不是被選中的課程中價值最低的,最優方案必定不會是選擇其餘課程,由於顯然不優;而若是是價值最低的,能夠枚舉全部其餘課程進行選擇。先二分一下答案就行了。it

#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)的,跑費用流便可。class

#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時即有解。gc

  考慮轉移,先將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

相關文章
相關標籤/搜索