2018.7.14 绍兴一中模拟赛 解题报告

成绩: 60+60+30=150(较差,很多该得的分没有拿到)


T1:城市(city)

满分解法:

这道题目说实话还真是挺水的。
按照我的思路,就是两遍DFS,第一遍预处理出每个节点的father深度以及子树大小,第二遍直接扫描每个点求答案。然后就可以水过去了。
然而,我有一个地方忘记开long long了。。。一下少了40分。。。(试求我的心理阴影面积)
代码如下:

#include
#define LL long long
#define N 1000000
#define MOD 1000000007
int n,ee=0,fa[N+5],lnk[N+5];
LL ans,dis,tot,Size[N+5];
struct edge
{
	int to,nxt;
}e[2*N+5];
inline char tc()
{
	static char ff[10000],*A=ff,*B=ff;
	return A==B&&(B=(A=ff)+fread(ff,1,10000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
	x=0;int f=1;char ch;
	while(!isdigit(ch=tc())) if(ch=='-') f=-1;;
	while(x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
	x*=f;
}
inline void write(LL x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
inline void add(int x,int y)
{
	e[++ee]=(edge){y,lnk[x]},lnk[x]=ee;
}
inline void dfs1(int x,int Depth)//第一遍DFS预处理
{
	register int i;Size[x]=x,++Depth;
	for(i=lnk[x];i;i=e[i].nxt)
		if(e[i].to^fa[x]) dis+=1LL*Depth*e[i].to,fa[e[i].to]=x,dfs1(e[i].to,Depth),Size[x]+=Size[e[i].to];//预处理出每个节点的father和子树大小,并用dis统计出每个节点到1号节点的距离和(就是这个地方我原本少打了个"1LL*",结果就炸了QwQ)
}
inline void dfs2(int x)
{
	for(int i=lnk[x];i;i=e[i].nxt)
	{
		if(e[i].to^fa[x])
		{
			LL Other=tot-Size[e[i].to],dis_=dis;//Other表示除当前子树外的总人数,dis_备份当前的dis,方便回溯
			(ans+=(1LL*((dis+=Other-Size[e[i].to])%=MOD<<1)*e[i].to)%(MOD<<1))%=MOD<<1,dfs2(e[i].to),dis=dis_;//移动之后,与该子树内每个点的距离减1了,但与该子树外每个点的距离加1了,因此可以快速更新dis,同时更新ans
		}
	}
}
int main()
{
	freopen("city.in","r",stdin),freopen("city.out","w",stdout);
	register int i;int x,y;
	for(read(n),i=1;i<n;i++)
		read(x),read(y),add(x,y),add(y,x);
	tot=(1LL*n*(n+1))>>1,dfs1(1,0);
	ans=dis,dfs2(1),write(((ans>>1)%MOD+MOD)%MOD);//因为两个点之间的距离被重复计算了两次,因此要将最终答案除以2;又因为最后要模一个数,因此在求解过程中我每次模时都将MOD乘了2(例如dfs2中更新ans的时候),最后再将ans除以2去模1倍的MOD
	return 0;
}

T2:人工智障(ai)

60分/70分解法:

很暴力的思想,直接暴力求答案,时间复杂度大概是 O ( l c m ( n , m ) ) O(lcm(n,m)) O(lcm(n,m)),这样60分就到手了。
当然,也有些大佬暴力得了70分(%%%常数之神hl666%%%)。

满分解法:

满分解法的思路就是将k分解为能整除 l c m ( n , m ) lcm(n,m) lcm(n,m)的部分及其余数。
对于能整除 l c m ( n , m ) lcm(n,m) lcm(n,m)的部分,一个很简单的方法就是暴力求解A和B各自赢的局数。不过,正解则稍微复杂一点。
g = g c d ( n , m ) g=gcd(n,m) g=gcd(n,m),则能与a[i]( 1 ≤ i ≤ g 1≤i≤g 1ig)匹配的只有b[i+kg](k为自然数)。所以,我们就能快速得到答案,再将答案乘 k / l c m ( n , m ) k/lcm(n,m) k/lcm(n,m)即可。
对于余数部分,也可以执行类似操作,不过要注意是否超出范围。
代码如下:

#include
#define LL long long
#define N 500000
#define M 500000
using namespace std;
LL n,m,k,a[N+5],b[M+5];
inline char tc()
{
	static char ff[100000],*A=ff,*B=ff;
	return A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(LL &x)
{
	x=0;LL f=1;char ch;
	while(!isdigit(ch=tc())) if(ch=='-') f=-1;
	while(x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
	x*=f;
}
inline void write(LL x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
inline LL gcd(LL x,LL y)
{
	return y?gcd(y,x%y):x;
}
inline LL lcm(LL x,LL y)
{
	return 1LL*x/gcd(x,y)*y;
}
int main()
{
	freopen("ai.in","r",stdin),freopen("ai.out","w",stdout);
	register LL i,j;
	for(read(n),read(m),read(k),i=1;i<=n;read(a[i++]));
	for(i=1;i<=m;read(b[i++]));
	LL g=gcd(n,m),w=lcm(n,m),WinA=0,WinB=0,s[3];
	for(i=1;i<=g;++i)
	{
		s[0]=s[1]=s[2]=0;//统计石头、剪刀、布出现的次数
		for(j=i;j<=n;j+=g) ++s[a[j]];
		for(j=i;j<=m;j+=g) WinA+=s[(b[j]+2)%3],WinB+=s[(b[j]+1)%3];//计算答案
	}
	WinA*=k/w,WinB*=k/w,k%=w;//将答案乘上k/lcm(n,m),并将k向lcm(n,m)取模
	if(k)//如果k不能整除lcm(n,m)
	{
		for(i=1;i<=g;++i)
		{
			s[0]=s[1]=s[2]=0;
			int o=i;//o记录当前的尾部
			for(j=1;j<=(k+n-1)/n;++j)
			{
				++s[b[o]];
				if(j<=(k-1)/n) ++((o+=n-1)%=m);
			}
			int x=0;
			for(j=i;j^x;++((j+=n-1)%=m),x=i)
			{
				for(register LL p=j;p<=n;p+=m)
				{
					if(p>(k-1)%n+1) --s[b[o]];//记得判断是否超出范围
					WinA+=s[(a[p]+1)%3],WinB+=s[(a[p]+2)%3];
					if(p>(k-1)%n+1) ++s[b[o]];
				}
				++s[b[++((o+=n-1)%=m)]],--s[b[j]];
			}
		}
	}
	return write(WinA),putchar(' '),write(WinB),0;
}

T3:循环(calc)

30分解法:

纯暴力+一点几乎不起任何作用的小优化的朴素算法,这样就能骗到30分的部分分了。

满分解法:

这题暂时还没有改满,等改满后再更新该部分的题解。

你可能感兴趣的:(比赛)