Forethought Future Cup

  A:签到。

#include<bits/stdc++.h>
using namespace std;
#define ll 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;char s[100];
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	scanf("%s",s+1);n=strlen(s+1);int cnt=0;
	for (int i=1;i<=n;i++) if (s[i]=='a') cnt++;
	for (int i=n;i>=1;i--) if (cnt>i/2) {cout<<i;break;} 
	return 0;
	//NOTICE LONG LONG!!!!!
}

  B:显然可能的划分位置只有一个,找到后检验即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 100010
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],a[N],b[N];
signed main()
{
	scanf("%s",s+1);n=strlen(s+1);int cnt=0;
	int cnt2=0;for (int i=1;i<=n;i++) if (s[i]=='a') cnt2++;
	for (int i=1;i<=n;i++)
	{
		if (s[i]=='a') cnt++;
		if (cnt==cnt2&&i-cnt==n-i)
		{
			int t=0;
			for (int j=1;j<=i;j++) if (s[j]!='a') a[++t]=s[j];
			t=0;
			for (int j=i+1;j<=n;j++) b[++t]=s[j];
			for (int j=1;j<=t;j++) if (a[j]!=b[j]) {cout<<":(";return 0;}
			for (int j=1;j<=i;j++) putchar(s[j]);return 0;
		}
	}
	cout<<":(";
	return 0;
	//NOTICE LONG LONG!!!!!
}

  C:二进制分组。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 100010
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 T,n,a[110],b[110];
signed main()
{
	T=read();
	while (T--)
	{
		n=read();
		int ans=0;
		for (int i=7;i>=0;i--)
		{
			int u=0,v=0;
			for (int j=1;j<=n;j++)
			if (j&(1<<i)) a[++u]=j;
			else b[++v]=j;
			if (u&&v)
			{
				cout<<u<<' '<<v<<' ';
				for (int j=1;j<=u;j++) cout<<a[j]<<' ';
				for (int j=1;j<=v;j++) cout<<b[j]<<' ';
				cout<<endl;
				ans=max(ans,read());
			}
		}
		cout<<-1<<' '<<ans<<endl;
	}
	return 0;
	//NOTICE LONG LONG!!!!!
}

  D:不考虑范围限制的话显然能到达所有gcd(a,b)的倍数。注意到对于不小于a+b的gcd(a,b)的倍数,该点一定可以为到达该点时所经过的最大值。因为可以考虑每次若能往左走就往左走,否则往右走,显然这样往右走的时候所达位置一定<=a+b,且这样可以到达0~a中所有gcd(a,b)的倍数,由这些点一直向右就可以到达所有gcd(a,b)的倍数了。那么对于<a+b的数暴力跑dij,>=a+b的数等差数列求和即可。然后因为inf设小调了一年。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define M 1000000
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,d[1000010];
bool flag[1000010];
struct data
{
	int x,d;
	bool operator <(const data&a) const
	{
		return d>a.d;
	}
};
priority_queue<data> q;
void dij()
{
	for (int i=1;i<=M;i++) d[i]=1000000000;d[0]=0;
	q.push((data){0,0});
	for (;;)
	{
		while (!q.empty()&&flag[q.top().x]) q.pop();
		if (q.empty()) break;
		data x=q.top();q.pop();
		flag[x.x]=1;
		if (x.x>=b&&d[x.x-b]>x.d)
		{
			d[x.x-b]=x.d;
			q.push((data){x.x-b,d[x.x-b]});
		}
		if (x.x+a<=M&&d[x.x+a]>max(x.x+a,x.d))
		{
			d[x.x+a]=max(x.x+a,x.d);
			q.push((data){x.x+a,d[x.x+a]});
		}
	}
}
signed main()
{
	n=read(),a=read(),b=read();
	dij();
	ll ans=0;
	for (int i=0;i<=min(n,M);i++) ans+=max(0,n-d[i]+1);
	int u=gcd(a,b),cnt=n/u-M/u;
	if (cnt>0)
	{
		ans+=1ll*cnt*(n+1);
		int l,r;
		for (l=M+1;l<=n;l++) if (l%u==0) break;
		for (r=n;r>M;r--) if (r%u==0) break;
		ans-=1ll*(l+r)*cnt/2;
	}
	//��i (i=1000001~n,i%u==0)
	cout<<ans;
	return 0;
	//NOTICE LONG LONG!!!!!
}

  E:考虑权值线段树维护每个数当前的符号情况。<x和>x类似,不妨只讨论<x。若x不为正数,相当于把最初<x和>-x的数强制变成正数,是区间覆盖。若x为正数,相当于把最初>=x和<=-x的数强制变成正数,并且将最初在(x,-x)的数符号取反,区间覆盖+区间取反。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1000000010
#define N 200010
#define V 100001
char getc(){char c=getchar();while (c!='<'&&c!='>') 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,a[N],tree[N<<2],tag[N<<2];
void build(int k,int l,int r)
{
	if (l==r) {tree[k]=1;return;}
	int mid=l+r>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
}
void down(int k)
{
	if (tree[k]) tree[k<<1]=tree[k<<1|1]=tree[k],tag[k<<1]=tag[k<<1|1]=0;
	else
	{
		if (tree[k<<1]) tree[k<<1]=-tree[k<<1];else tag[k<<1]^=1;
		if (tree[k<<1|1]) tree[k<<1|1]=-tree[k<<1|1];else tag[k<<1|1]^=1;
	}
	tree[k]=tag[k]=0;
}
void cover(int k,int l,int r,int x,int y,int p)
{
	if (l==x&&r==y) {tree[k]=p;tag[k]=0;return;}
	if (tree[k]||tag[k]) down(k);
	int mid=l+r>>1;
	if (y<=mid) cover(k<<1,l,mid,x,y,p);
	else if (x>mid) cover(k<<1|1,mid+1,r,x,y,p);
	else cover(k<<1,l,mid,x,mid,p),cover(k<<1|1,mid+1,r,mid+1,y,p);
}
int query(int k,int l,int r,int x)
{
	if (l==r) return tree[k];
	if (tree[k]||tag[k]) down(k);
	int mid=l+r>>1;
	if (x<=mid) return query(k<<1,l,mid,x);
	else return query(k<<1|1,mid+1,r,x);
}
void rev(int k,int l,int r,int x,int y)
{
	if (l==x&&r==y) {if (tree[k]) tree[k]=-tree[k];else tag[k]^=1;return;}
	if (tree[k]||tag[k]) down(k);
	int mid=l+r>>1;
	if (y<=mid) rev(k<<1,l,mid,x,y);
	else if (x>mid) rev(k<<1|1,mid+1,r,x,y);
	else rev(k<<1,l,mid,x,mid),rev(k<<1|1,mid+1,r,mid+1,y);
}
void Cover(int l,int r,int x){cover(1,1,2*V+1,l+V+1,r+V+1,x);}
int Query(int x){return query(1,1,2*V+1,x+V+1);}
void Rev(int l,int r){rev(1,1,2*V+1,l+V+1,r+V+1);}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	n=read(),m=read();
	for (int i=1;i<=n;i++) a[i]=read();
	build(1,1,2*V+1);
	while (m--)
	{
		char c=getc();int x=read();
		if (c=='<')
		{
			if (x<=0) Cover(-V,x-1,-1),Cover(-x+1,V,1);
			else Cover(-V,-x,-1),Cover(x,V,1),Rev(-x+1,x-1);
		}
		else
		{
			if (x>=0) Cover(-V,-x-1,1),Cover(x+1,V,-1);
			else Cover(-V,x,1),Cover(-x,V,-1),Rev(x+1,-x-1);
		}
	//	for (int i=1;i<=n;i++) printf("%d ",Query(a[i])*a[i]);cout<<endl;
	}
	for (int i=1;i<=n;i++) printf("%d ",Query(a[i])*a[i]);
	return 0;
	//NOTICE LONG LONG!!!!!
}

  F:这种看上去特别弱智但特别容易假的题好烦啊。就不写心路历程了因为我也不知道怎么假着假着就真了。

  对于当前考虑的子树,显然是在儿子子树中选一些集合并将根加入,但是如果根在其中只属于某一个儿子子树的集合,不一定构成合法方案。于是就来乱七八糟的dp。

  设f[i][0/1/2]为i子树内根被强制分配(即只属于某个儿子子树的集合)的方案数/被非强制分配(即属于多个儿子子树的集合)的方案数/i子树的答案。考虑转移。

  显然f[i][2]是可以由f[i][1]得到的,相当于是选择一些点,使其相互不包含,且其是子树内所有叶子的lca,且其覆盖了所有叶子,这些点f[i][1]的乘积,所有选择点的方案的乘积之和。有点绕但意义很好理解。

  然后考虑f[i][0]的转移。考虑将根分配进哪个儿子子树的集合,此时该儿子子树的方案数应该是f[son][0]+f[son][1],而其他儿子子树的方案数是f[son][2]。求逆元(似乎不太会出现0?反正过掉了)或者求前后缀积即可。

  最后是f[i][1]。直接算不太行,考虑补集转化,不管根是否是被强制拉进来的话方案数就是∏((f[son][0]+f[son][1])+f[son][2]),意义与f[i][0]时的类似,前一项是在包含根的集合内,后一项不是。然后去掉不合法方案,可以发现是f[i][0]+∏f[son][2],∏f[son][2]对应根不在任意一个集合,f[i][0]对应根只在一个集合。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 200010
#define P 998244353
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,fa[N],p[N],f[N][3],t;
struct data{int to,nxt;
}edge[N];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
int ksm(int a,int k)
{
	int s=1;
	for (;k;k>>=1,a=1ll*a*a%P) if (k&1) s=1ll*s*a%P;
	return s;
}
int inv(int a){return ksm(a,P-2);}
void dfs(int k)
{
	int s=0;for (int i=p[k];i;i=edge[i].nxt) s++;
	f[k][1]=1;f[k][2]=1;
	for (int i=p[k];i;i=edge[i].nxt)
	{
		dfs(edge[i].to);
		f[k][1]=1ll*f[k][1]*((f[edge[i].to][0]+f[edge[i].to][1])%P+f[edge[i].to][2])%P;
		f[k][2]=1ll*f[k][2]*f[edge[i].to][2]%P;
	}
	if (s)
	{
		for (int i=p[k];i;i=edge[i].nxt)
		{
			int x=1ll*inv(f[edge[i].to][2])*f[k][2]%P*(f[edge[i].to][0]+f[edge[i].to][1])%P;
			f[k][0]=(f[k][0]+x)%P;
		}
		f[k][1]=(f[k][1]-f[k][0]+P)%P;
		f[k][1]=(f[k][1]-f[k][2]+P)%P;
	}
	else f[k][0]=0,f[k][1]=1,f[k][2]=0;
	f[k][2]=(f[k][2]+f[k][1])%P;
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	const char LL[]="%I64d
";
#else
	const char LL[]="%lld
";
#endif
	n=read();
	for (int i=2;i<=n;i++) fa[i]=read(),addedge(fa[i],i);
	dfs(1);//for (int i=1;i<=n;i++) cout<<f[i][0]<<' '<<f[i][1]<<' '<<f[i][2]<<endl; 
	cout<<f[1][2];
	return 0;
}

  G:只要做过切糕并且知道这题可以网络流,很容易就能想到做法。考虑最小割,每个位置建一条链的点表示高度,割掉链上某条边就表示选择该高度,其中边权为h2-h'2。对于每个限制建一个点,将限制区间内所有点的对应高度向该点连inf边,该点向汇连边权为代价的边,直接跑最小割即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 55
#define S 0
#define T 3000
#define inf N*N*N
char getc(){char c=getchar();while (c!='<'&&c!='>') 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,h,p[N*N],d[N*N],cur[N*N],q[N*N],id[N][N],cnt,t=-1,ans;
struct data{int to,nxt,cap,flow;
}edge[N*N<<2];
void addedge(int x,int y,int z)
{
	t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].cap=z,edge[t].flow=0,p[x]=t;
	t++;edge[t].to=x,edge[t].nxt=p[y],edge[t].cap=0,edge[t].flow=0,p[y]=t;
}bool bfs()
{
    memset(d,255,sizeof(d));d[S]=0;
    int head=0,tail=1;q[1]=S;
    do
    {
        int x=q[++head];
        for (int i=p[x];~i;i=edge[i].nxt)
        if (d[edge[i].to]==-1&&edge[i].flow<edge[i].cap)
        {
            d[edge[i].to]=d[x]+1;
            q[++tail]=edge[i].to;
        }
    }while (head<tail);
    return ~d[T];
}
int work(int k,int f)
{
    if (k==T) return f;
    int used=0;
    for (int i=cur[k];~i;i=edge[i].nxt)
    if (d[k]+1==d[edge[i].to])
    {
        int w=work(edge[i].to,min(f-used,edge[i].cap-edge[i].flow));
        edge[i].flow+=w,edge[i^1].flow-=w;
        if (edge[i].flow<edge[i].cap) cur[k]=i;
        used+=w;if (used==f) return f;
    }
    if (used==0) d[k]=-1;
    return used;
}
void dinic()
{
    while (bfs())
    {
        memcpy(cur,p,sizeof(p));
        ans+=work(S,inf);
    }
}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	n=read(),h=read(),m=read();
	memset(p,255,sizeof(p));
	for (int i=1;i<=n;i++)
		for (int j=1;j<=h;j++)
		id[i][j]=++cnt;
	for (int i=1;i<=n;i++)
	{
		addedge(S,id[i][1],h*h);
		for (int j=1;j<h;j++)
		addedge(id[i][j],id[i][j+1],h*h-j*j);
	}
	for (int i=1;i<=m;i++)
	{
		int l=read(),r=read(),x=read(),c=read();
		if (x<h)
		{
			cnt++;
			for (int j=l;j<=r;j++)
			addedge(id[j][x+1],cnt,inf);
			addedge(cnt,T,c);
		}
	}
	dinic();
	cout<<n*h*h-ans;
	return 0;
	//NOTICE LONG LONG!!!!!
}

  或者可以区间dp,设f[i][j][k]为i~j区间最大值不超过k时,只考虑完全被该区间包含的限制,最大价值是多少。转移枚举最高点所在位置,计算跨过该点的代价,然后两边就可以被划分为子问题。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 55
#define S 0
#define T 3000
char getc(){char c=getchar();while (c!='<'&&c!='>') 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,h,f[N][N][N];
struct data{int l,r,x,c;
}lim[N];
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	n=read(),h=read(),m=read();
	for (int i=1;i<=m;i++) lim[i].l=read(),lim[i].r=read(),lim[i].x=read(),lim[i].c=read();
	for (int k=1;k<=n;k++)
		for (int l=1;l<=n-k+1;l++)
		{
			int r=l+k-1;
			for (int x=0;x<=h;x++)
				for (int d=l;d<=r;d++)
				{
					int tot=x*x;
					for (int _=1;_<=m;_++)
					if (lim[_].l>=l&&lim[_].r<=r&&lim[_].l<=d&&lim[_].r>=d&&lim[_].x<x) tot-=lim[_].c;
					f[l][r][x]=max(f[l][r][x],tot+f[l][d-1][x]+f[d+1][r][x]);
				}
			for (int x=1;x<=h;x++) f[l][r][x]=max(f[l][r][x],f[l][r][x-1]);
		}
	cout<<f[1][n][h];
	return 0;
	//NOTICE LONG LONG!!!!!
}

  H:显然相当于求五个点的凸包数量。考虑枚举上顶点,将其他点按极角排序。要满足是凸包,只需要相邻两点所成射线的斜率依次递增(这里取atan2值作为斜率)即可。设凸包上的点逆时针排列依次为abcde,其中a为上顶点。对于每个点,求出其作为c时所有能与其构成凸包的b,将其构成的射线按斜率排序。同理求出其作为d时的所有e。然后枚举cd,根据cd斜率在两个已排序的斜率数组中二分即可。我觉得比D简单

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1000000010
#define N 310
#define vector point
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,down_cnt[N],up_cnt[N];
ll ans;
double down[N][N],up[N][N];
struct point
{
	int x,y;
	vector operator +(const vector&a) const
	{
		return (vector){x+a.x,y+a.y};
	}
	vector operator -(const vector&a) const
	{
		return (vector){x-a.x,y-a.y};
	}
	ll operator *(const vector&a) const
	{
		return 1ll*x*a.y-1ll*y*a.x;
	}
}a[N],b[N],O;
double alpha(point x)
{
	return atan2(x.y,x.x);
}
bool cmp1(const point&a,const point&b)
{
	return a.y>b.y;
}
bool cmp2(const point&a,const point&b)
{
	return alpha(a)<alpha(b);
}
bool onright(point a,point b,point c)
{
	return (c-a)*(b-a)>0;
}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	n=read();
	for (int i=1;i<=n;i++) a[i].x=read(),a[i].y=read();
	sort(a+1,a+n+1,cmp1);
	for (int i=1;i<=n;i++)
	{
		m=0;
		for (int j=i+1;j<=n;j++) b[++m]=a[j]-a[i];
		sort(b+1,b+m+1,cmp2);
		for (int j=1;j<=m;j++)
		{
			down_cnt[j]=0;
			for (int k=1;k<j;k++)
			if (onright(O,b[j],b[k])) down[j][++down_cnt[j]]=alpha(b[j]-b[k]);
			sort(down[j]+1,down[j]+down_cnt[j]+1);
			up_cnt[j]=0;
			for (int k=j+1;k<=m;k++)
			if (onright(O,b[k],b[j])) up[j][++up_cnt[j]]=alpha(b[k]-b[j]);
			sort(up[j]+1,up[j]+up_cnt[j]+1);
		}
		for (int j=1;j<=m;j++)
			for (int k=j+1;k<=m;k++)
			{
				int x=lower_bound(down[j]+1,down[j]+down_cnt[j]+1,alpha(b[k]-b[j]))-down[j]-1;
				int y=up_cnt[k]-(lower_bound(up[k]+1,up[k]+up_cnt[k]+1,alpha(b[k]-b[j]))-up[k])+1;
				//cout<<a[i].x<<' '<<a[i].y<<"  "<<b[j].x<<' '<<b[j].y<<"  "<<b[k].x<<' '<<b[k].y<<"  "<<x<<' '<<y<<endl;
				ans+=1ll*x*y;
			}
	}
	cout<<ans;
	return 0;
	//NOTICE LONG LONG!!!!!
}

  

原文地址:https://www.cnblogs.com/Gloid/p/10747861.html