这一场的题目都很有趣。我比赛的时候第三题样例画错,然而题目也理解错了,所以就居然算出正解。这太巧合了,打的也很烂。心态也很糟糕。还有第一题,读题漏信息真的好多。就下次思维别这么跳跃吧。然后T2(有趣的题目)一开始想就想错,还感觉很对就离谱。我没有用题目已知条件映射的数学柿子来变形,而是自己凭感觉凑与推。我觉的我目前的水平想对思路真的很难。第四题,数据结构第一步的转换都想不到,更别说后面的板子。这个转化也挺有趣的。

T1 饥饿的狐狸

  • 题意:你有n块饼干每块温度Ti,你还有W的水,你的狐狸喜欢舌间的冷热刺激,每次吃饼干的美味度是上一次于这一次温度差的绝对值。问吃完饼干的最大和最小美味值。

  • 思路:

  • 代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
ll t[N];
int main() {
	int n;ll w;
	scanf("%d%lld",&n,&w);
	for(int i=1;i<=n;i++) scanf("%lld",&t[i]);
	sort(t+1,t+1+n);
	printf("%lld ",max(0ll,w-t[1])+max(0ll,t[n]-w));
	int l=1,r=n; ll mx1=0,mx2=0,lst=w;
	for(int i=1;i<=n;i++) {
		if(i&1) mx1+=max(abs(t[l]-w),abs(t[l]-lst)),lst=t[l++];
		else mx1+=max(abs(t[r]-w),abs(t[r]-lst)),lst=t[r--];
	}
	l=1,r=n,lst=w;
	for(int i=1;i<=n;i++) {
		if(i&1) mx2+=max(abs(t[r]-w),abs(t[r]-lst)),lst=t[r--];
		else mx2+=max(abs(t[l]-w),abs(t[l]-lst)),lst=t[l++];
	}
	printf("%lld",max(mx1,mx2));
	return 0;
}

T2保险箱

  • 题意:略

这道题我后面会写的很啰嗦,因为很有趣

  • 思路:我们考虑到一个显然的性质:mktmodn 为可以打开的,我考场上就想到这点然后就想偏了。
    首先这道题就两种条件(一个正确的,和一堆错误的),条件越少,我们就要将其挖掘越深。
    我们设最后可行的集合为S.
    d=gcd(Si,n)[iS]
  • 正确
    xmkc(modn) c为一定可行的解
    c=xmk+yn
    因此已经能确定的最小的S内元素为 gcd(mk,n)
    第一个重要性质:d|gcd(mk,n)
  • 错误
    iSixmj(modn)
    第二个条件:
    dmj
    答案为n/d
    ps.为什么d的倍数不能取模,因为一开始d的定义就直接表示最终状态的gcd
    因此我们的需求是最小的d
    然后我们先将mk=gcd(mk,n) , mj=gcd(mj,mk)
    此时mj内只有mk种也有的因子,我们将其从mk中筛掉,此时d便为mk中的最小因子。
    具体实现中我们发现mk最多20个质因子,把它们筛出来,每个mj筛的时候,用map记忆化搜索一下。
    复杂度为sqrt(mk)20上限约10520
  • 代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
ll p[N],c[N],m[N],tot;
set<ll> S;
map<ll,bool>mp;
ll gcd(ll u,ll v) {
	if(!v) return u;
	return gcd(v,u%v);
}
void dfs(ll x) {
	if(mp[x])return; mp[x]=1;
	for(int i=1;i<=tot;i++) if(x%p[i]==0)dfs(x/p[i]);
}
int main() {
	ll n;int k;
	scanf("%lld%d",&n,&k);
	for(int i=1;i<=k;i++) scanf("%lld",&m[i]);
	m[k]=gcd(m[k],n);
	for(int i=1;i<k;i++) m[i]=gcd(m[i],m[k]);
	ll x=m[k];
	for(ll i=2;i*i<=x;i++) {
		if(x%i==0) {
			p[++tot]=i;
			while(x%i==0) {x/=i;}
		}
	}
	if(x>1) {p[++tot]=x;}
	for(int i=1;i<k;i++)dfs(m[i]);
	ll i;
	for(i=1;i*i<=m[k];i++) if(m[k]%i==0&&!mp[i]) {printf("%lld",n/i);return 0;}
	for(;i;i--) if(m[k]%i==0&&!mp[m[k]/i]) {printf("%lld",n/(m[k]/i));return 0;}
	return 0;
}

Chase

  • 题解:
    显然拿一维记录剩余磁铁数
    t[i][j]表示从i到子树内部最大,p[i][j]表示从子树内到i
    转移见代码
    为什么这样?因为走的方向会影响,比如你放磁铁会影响你的父亲而不是孩子。(考试上就tm没想全)
    写代码中有一些细节:
    1.更新注意不能重u,然后不能到达两个相同的子树
    2.按1来写,要正反来两遍
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
const int M=105;
int n,V,head[N<<1],to[N<<1],nxt[N<<1],ecnt;
ll sum[N],ans,val[N],t[N][M],p[N][M],mx2[M];
void add_edge(int u,int v) {nxt[++ecnt]=head[u];to[ecnt]=v;head[u]=ecnt;}
void dfs(int u,int fa) {
	int tp=0,st[N];
	for(int j=1;j<=V;j++) t[u][j]=sum[u]-val[fa],p[u][j]=sum[u];
	for(int i=head[u];i;i=nxt[i]) {
		int v=to[i];
		if(v==fa)continue;
		dfs(v,u);st[++tp]=v;
		for(int j=1;j<=V;j++)ans=max(ans,p[u][j]+t[v][V-j]);
		for(int j=1;j<=V;j++) {
			t[u][j]=max(t[u][j],max(t[v][j],t[v][j-1]+sum[u]-val[fa]));
			p[u][j]=max(p[u][j],max(p[v][j],p[v][j-1]+sum[u]-val[v]));
		}
	}
	for(int j=1;j<=V;j++) t[u][j]=sum[u]-val[fa],p[u][j]=sum[u];
	for(int i=tp;i>=1;i--) {
		int v=st[i];
		for(int j=1;j<=V;j++)ans=max(ans,p[u][j]+t[v][V-j]);
		for(int j=1;j<=V;j++) {
			t[u][j]=max(t[u][j],max(t[v][j],t[v][j-1]+sum[u]-val[fa]));
			p[u][j]=max(p[u][j],max(p[v][j],p[v][j-1]+sum[u]-val[v]));
		}
	}
	ans=max(ans,max(t[u][V],p[u][V]));
}
int main() {
	scanf("%d%d",&n,&V);
	for(int i=1;i<=n;i++) scanf("%lld",&val[i]);
	for(int i=1;i<n;i++) {
		int u,v;
		scanf("%d%d",&u,&v);
		sum[u]+=val[v],sum[v]+=val[u];
		add_edge(u,v),add_edge(v,u);
	}
	dfs(1,0);
	printf("%lld",ans);
	return 0;
}

Election

  • 思路:考虑这样一种简单的贪心,我们从前向后扫一遍,如果前缀小于 0 就删掉当前字符,在从后向前扫一遍即可。

  • 代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
char ch[N];
int Q,n,a[N],s[N];
struct seg {int l,r,mx,lx,rx,sum;}T[N<<2];
void P_up(int x) {
	int ls=x<<1,rs=x<<1|1;
	T[x].sum=T[ls].sum+T[rs].sum;
	T[x].rx=max(T[rs].rx,T[rs].sum+T[ls].rx);
	T[x].lx=max(T[ls].lx,T[ls].sum+T[rs].lx);
	T[x].mx=max(T[ls].rx+T[rs].lx,max(T[ls].mx,T[rs].mx));
}
void Build(int x,int l,int r) {
	T[x]=(seg){l,r,0,0,0};
	if(l==r) {T[x].lx=T[x].rx=T[x].mx=a[l];T[x].sum=a[l];return;}
	int mid=(l+r)>>1;
	Build(x<<1,l,mid),Build(x<<1|1,mid+1,r);
	P_up(x);
}
seg Mx(int x,int l,int r) {
	if(l<=T[x].l&&T[x].r<=r) return T[x];
	int mid=(T[x].l+T[x].r)>>1,res=0;
	seg u,v,w;
	if(l<=mid) {
		u=Mx(x<<1,l,r);
		if(r<=mid) return u;
	}
	if(r>mid) {
		v=Mx(x<<1|1,l,r);
		if(l>mid) return v;
	}
	w.sum=u.sum+v.sum;
	w.rx=max(v.rx,v.sum+u.rx);
	w.lx=max(u.lx,u.sum+v.lx);
	w.mx=max(u.rx+v.lx,max(u.mx,v.mx));
	return w;
}
int main() {
	scanf("%d",&n);
	scanf("%s",ch);
	for(int i=0;i<n;i++) a[i+1]=(ch[i]=='C')?1:-1;
	for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i];
	Build(1,1,n);
	scanf("%d",&Q);
	while(Q--) {
		int l,r;
		scanf("%d%d",&l,&r);
		int ss=s[r]-s[l-1];
		printf("%lld\n",max(Mx(1,l,r).mx,0)-ss);
	}
	return 0;
}