加载中…

返回上一页

CSP-S加赛0915

题面和题解

非常不解,当时考试最后10分钟的时候开始看的第2题,顺手打了一个 lca 加上暴力 dfs 还有个20分,不知道那些两个题都得0分的是怎么搞得.

A. 山洞

输出0就有40分,出题人竟如此良心.

明显是一个dp可是我最开始看还是想着用组合数来着.

dp[i][j]表示在第 i 步到达 j 的方案数. 由于第 i 步要走 i 个点,则转移很简单:

边界显然 dp[0][1]=dp[0][n-1]=1,记得 j-ij+i 不要越界,加/减 n 就行了.

数组开不下,那就滚一下.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define maxn 10001
#define mod 1000000007
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
static inline ll read()
{
	rll f=0,x=0;rg char ch=getchar();
	while(ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
	while(ch>='0'&&ch<='9') x=((x<<3))+((x<<1))+(ch^'0'),ch=getchar();
	return f?-x:x;
}
static inline void write(rll x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);putchar(x%10|'0');
}
ll n,m;
ll kk,dp[2][maxn]/*滚动数组*/;
ll bzh/*变正*/(rll x) { while(x&LLONG_MIN) x+=n; return x; }
int main()
{
	n=read();m=read();dp[0][1]=dp[0][n-1]=1;
	for(rll i=2;i<=m;i++)
	{
		kk^=1;
		for(rll j=0;j<n;j++) dp[kk][j]=(dp[kk^1][bzh(j-i)]+dp[kk^1][(j+i)%n])%mod;
	}
	write(dp[kk][0]);
	return 0;
}

然后你就有了60分的好成绩.

详细信息
子任务 #1
Time Limit Exceeded
得分:60
测试点 #1
Accepted
得分:100
用时:3 ms
内存:388 KiB

输入文件(cave1.in

455 6

答案文件(cave1.out

0

用户输出

0

系统信息

Exited with return code 0
测试点 #2
Accepted
得分:100
用时:2 ms
内存:388 KiB

输入文件(cave2.in

349 6

答案文件(cave2.out

0

用户输出

0

系统信息

Exited with return code 0
测试点 #3
Accepted
得分:100
用时:5 ms
内存:388 KiB

输入文件(cave3.in

873 847

答案文件(cave3.out

168562759

用户输出

168562759

系统信息

Exited with return code 0
测试点 #4
Accepted
得分:100
用时:3 ms
内存:356 KiB

输入文件(cave4.in

641 312

答案文件(cave4.out

184576581

用户输出

184576581

系统信息

Exited with return code 0
测试点 #5
Accepted
得分:100
用时:4 ms
内存:424 KiB

输入文件(cave5.in

873 464

答案文件(cave5.out

782238569

用户输出

782238569

系统信息

Exited with return code 0
测试点 #6
Accepted
得分:100
用时:2 ms
内存:376 KiB

输入文件(cave6.in

501 256

答案文件(cave6.out

618778073

用户输出

618778073

系统信息

Exited with return code 0
测试点 #7
Time Limit Exceeded
得分:0
用时:1043 ms
内存:388 KiB

输入文件(cave7.in

303 43643612

答案文件(cave7.out

3328565
测试点 #8
Time Limit Exceeded
得分:0
用时:1046 ms
内存:380 KiB

输入文件(cave8.in

216 223365721

答案文件(cave8.out

0
测试点 #9
Time Limit Exceeded
得分:0
用时:1040 ms
内存:476 KiB

输入文件(cave9.in

793 204192683

答案文件(cave9.out

667243642
测试点 #10
Time Limit Exceeded
得分:0
用时:1043 ms
内存:416 KiB

输入文件(cave10.in

770 275829513

答案文件(cave10.out

0

考虑优化. 发现中间的过程许多都是重复的,因此只需要求出前 n 步和后 m%ndp 值,中间 部分直接矩阵快速幂即可.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define maxn 10001
#define mod 1000000007
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
static inline ll read()
{
	rll f=0,x=0;rg char ch=getchar();
	while(ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
	while(ch>='0'&&ch<='9') x=((x<<3))+((x<<1))+(ch^'0'),ch=getchar();
	return f?-x:x;
}
static inline void write(rll x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);putchar(x%10|'0');
}
ll n,m;
ll kk,dp[2][maxn],tmp[maxn],ans[maxn]={1,0};
ll bzh/*变正*/(rll x) { while(x&LLONG_MIN) x+=n; return x; }
static inline void mulans()
{
	for(rll i=0;i<n;i++) tmp[i]=ans[i],ans[i]=0;
	for(rll i=0;i<n;i++) for(rll j=0;j<n;j++) ans[(i+j)%n]=(ans[(i+j)%n]+tmp[i]*dp[kk][j]%mod)%mod;
}
static inline void mula()
{
	for(rll i=0;i<n;i++) tmp[i]=dp[kk][i],dp[kk][i]=0;
	for(rll i=0;i<n;i++) for(rll j=0;j<n;j++) dp[kk][(i+j)%n]=(dp[kk][(i+j)%n]+tmp[i]*tmp[j]%mod)%mod;
}
static inline void ksm(rll b)
{
	for(rll i=b;i;i>>=1) { if(i&1) mulans();mula(); }
	for(rll i=0;i<n;i++) dp[0][i]=ans[i];
}
int main()
{
	n=read();m=read();dp[0][1]=dp[0][n-1]=1;
	for(rll i=2;i<=n;i++)
	{
		kk^=1;
		for(rll j=0;j<n;j++) dp[kk][j]=(dp[kk^1][bzh(j-i)]+dp[kk^1][(j+i)%n])%mod;
	}
	ksm(/*dp[kk],*/m/n);kk=0;
	for(rll i=1;i<=m%n;i++)
	{
		kk^=1;
		for(rll j=0;j<n;j++) dp[kk][j]=(dp[kk^1][bzh(j-i)]+dp[kk^1][(j+i)%n])%mod;
	}
	write(dp[kk][0]);
	return 0;
}

然后你有了80分的好成绩.

详细信息
子任务 #1
Wrong Answer
得分:80
测试点 #1
Accepted
得分:100
用时:2 ms
内存:388 KiB

输入文件(cave1.in

455 6

答案文件(cave1.out

0

用户输出

0

系统信息

Exited with return code 0
测试点 #2
Accepted
得分:100
用时:2 ms
内存:388 KiB

输入文件(cave2.in

349 6

答案文件(cave2.out

0

用户输出

0

系统信息

Exited with return code 0
测试点 #3
Accepted
得分:100
用时:7 ms
内存:392 KiB

输入文件(cave3.in

873 847

答案文件(cave3.out

168562759

用户输出

168562759

系统信息

Exited with return code 0
测试点 #4
Accepted
得分:100
用时:4 ms
内存:404 KiB

输入文件(cave4.in

641 312

答案文件(cave4.out

184576581

用户输出

184576581

系统信息

Exited with return code 0
测试点 #5
Accepted
得分:100
用时:7 ms
内存:448 KiB

输入文件(cave5.in

873 464

答案文件(cave5.out

782238569

用户输出

782238569

系统信息

Exited with return code 0
测试点 #6
Accepted
得分:100
用时:4 ms
内存:452 KiB

输入文件(cave6.in

501 256

答案文件(cave6.out

618778073

用户输出

618778073

系统信息

Exited with return code 0
测试点 #7
Wrong Answer
得分:0
用时:12 ms
内存:396 KiB

输入文件(cave7.in

303 43643612

答案文件(cave7.out

3328565

用户输出

106605750

Special Judge 信息

Files user_out and answer differ

系统信息

Exited with return code 0
测试点 #8
Accepted
得分:100
用时:9 ms
内存:408 KiB

输入文件(cave8.in

216 223365721

答案文件(cave8.out

0

用户输出

0

系统信息

Exited with return code 0
测试点 #9
Wrong Answer
得分:0
用时:72 ms
内存:436 KiB

输入文件(cave9.in

793 204192683

答案文件(cave9.out

667243642

用户输出

620222038

Special Judge 信息

Files user_out and answer differ

系统信息

Exited with return code 0
测试点 #10
Accepted
得分:100
用时:79 ms
内存:444 KiB

输入文件(cave10.in

770 275829513

答案文件(cave10.out

0

用户输出

0

系统信息

Exited with return code 0

可以发现,是 WA.
那么肯定是有哪里出错了.

在累加 dp 值的时候,如果出现当前点和下一个点正好在环的两端,即:

这种情况显然只需要加一遍就可以,所以加一个特判即可.

然后就可以愉快得 AC 了!

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define maxn 10001
#define mod 1000000007
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
static inline ll read()
{
	rll f=0,x=0;rg char ch=getchar();
	while(ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
	while(ch>='0'&&ch<='9') x=((x<<3))+((x<<1))+(ch^'0'),ch=getchar();
	return f?-x:x;
}
static inline void write(rll x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);putchar(x%10|'0');
}
ll n,m;
ll kk,dp[2][maxn],tmp[maxn],ans[maxn]={1,0};
ll bzh/*变正*/(rll x) { while(x&LLONG_MIN) x+=n; return x; }
static inline void mulans()
{
	for(rll i=0;i<n;i++) tmp[i]=ans[i],ans[i]=0;
	for(rll i=0;i<n;i++) for(rll j=0;j<n;j++) ans[(i+j)%n]=(ans[(i+j)%n]+tmp[i]*dp[kk][j]%mod)%mod;
}
static inline void mula()
{
	for(rll i=0;i<n;i++) tmp[i]=dp[kk][i],dp[kk][i]=0;
	for(rll i=0;i<n;i++) for(rll j=0;j<n;j++) dp[kk][(i+j)%n]=(dp[kk][(i+j)%n]+tmp[i]*tmp[j]%mod)%mod;
}
static inline void ksm(/*rll a[],*/rll b)
{
	for(rll i=b;i;i>>=1) { if(i&1) mulans();mula(); }
	for(rll i=0;i<n;i++) dp[0][i]=ans[i];
}
int main()
{
	n=read();m=read();dp[0][1]=dp[0][n-1]=1;
	for(rll i=2;i<=n;i++)
	{
		kk^=1;
		for(rll j=0;j<n;j++)
			if((j-i+n)%n==(i+j)%n) dp[kk][j]=dp[kk^1][(i+j)%n];
			else dp[kk][j]=(dp[kk^1][bzh(j-i)]+dp[kk^1][(i+j)%n])%mod;
	}
	ksm(/*dp[kk],*/m/n);kk=0;
	for(rll i=1;i<=m%n;i++)
	{
		kk^=1;
		for(rll j=0;j<n;j++)
			if((j-i+n)%n==(i+j)%n) dp[kk][j]=dp[kk^1][(i+j)%n];
			else dp[kk][j]=(dp[kk^1][bzh(j-i)]+dp[kk^1][(i+j)%n])%mod;
	}
	write(dp[kk][0]);
	return 0;
}

B.beauty

手模一下,可以轻易证明:

若每个点的子树(包括它自己)中关键点数量为 cnt[i],显然另一个方向中关键点数量为 k-cnt[i]. 那么答案就是:

.

因为每一个点的路径数加到一起必定是总的最多的路径数.

具体如果想要构造方法就是找出树的重心,从重心开始由近及远把 i 关键点与 i+k 配对.

点击查看代码
#include<bits/stdc++.h>
#ifndef ONLINE_JUDGE
#define ll long long
#define rg register
#define rll rg ll
#define maxn 100001
#define put_ putchar(' ')
#define putn putchar('\a')
using namespace std;
static inline ll read()
{
	rll f=0,x=0;rg char ch=getchar();
	while(ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
	while(ch>='0'&&ch<='9') x=((x<<3))+((x<<1))+(ch^'0'),ch=getchar();
	return f?-x:x;
}
static inline void write(rll x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);putchar(x%10|'0');
}
ll n,k,ans;
vector<ll> g[maxn];
ll cnt[maxn];
bool fl[maxn];
static inline void dfs1(ll x,ll fa)
{
	cnt[x]=fl[x];
	for(rll i=0;i<g[x].size();i++)
	{
		rll to=g[x][i];if(to==fa) continue;
		dfs1(to,x);cnt[x]+=cnt[to];
	}
}
int main()
{
	n=read();k=read();read();for(rll i=1;i<=(k<<1);i++) fl[read()]=1;
	for(rll i=1,u,v;i<n;i++) u=read(),v=read(),g[u].push_back(v),g[v].push_back(u);
	dfs1(1,0);
	for(rll i=1;i<=n;i++) ans+=min(cnt[i],(k<<1)-cnt[i]);
	write(ans);
	return 0;
}
posted @ 2022-09-15 21:31  1Liu  阅读(17)  评论(0)    收藏  举报