[WC2019]数树

Description

有两棵n个点的树,T1和T2,每个点可以填一个[1,y]的颜色
若两棵树有一条边重合这条边两个端点的颜色必须相同
有三种问题:
op=0,给出T1和T2问答案
op=1,给出T1问所有T2的答案和
op=2,给出n问所有的T1和T2的答案和
n<=100000

Solution

op=0大家都会
op=1的话,考虑算恰好有i条重边的y−iy^{-i}yi的和
考虑到zm=∑i=0m(z−1)i(im)z^m=\sum_{i=0}^{m}(z-1)^i(^m_i)zm=i=0m(z1)i(im)
于是我们可以变成,枚举i条重边,其余边随意的(z-1)^i的和
i条边会把原树分成n-i个连通块,设ai为连通块大小,根据purfer序我们可以推出生成树个数为nn−i−2∏ain^{n-i-2}\prod ainni2ai
考虑到∏ai\prod aiai的组合意义为从每个连通块里选一个点的方案数
设F[x][0…1]表示当前x为根的连通块有没有选这个点
直接Dp是O(n)的
op=2根据op=1断掉每条重边求出一个连通块的EGF然后exp一下就好了

Code

#include <map>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a) for(int i=lst[a];i;i=nxt[i])
using namespace std;

typedef long long ll;

int read() {
	char ch;
	for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
	int x=ch-'0';
	for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x;
}

const int N=1e6+5,Mo=998244353;

int pwr(int x,int y) {
	int z=1;
	for(;y;y>>=1,x=(ll)x*x%Mo)
		if (y&1) z=(ll)z*x%Mo;
	return z;
}

map<int,int> e[N];

int n,y,z;

void solve_0() {
	fo(i,1,n-1) {
		int x=read(),y=read();
		e[x][y]=e[y][x]=1;
	}
	int cnt=0;
	fo(i,1,n-1) {
		int x=read(),y=read();
		cnt+=e[x][y];
	}
	printf("%d\n",pwr(y,n-cnt));
}

int t[N],nxt[N],lst[N],l;
void add(int x,int y) {t[++l]=y;nxt[l]=lst[x];lst[x]=l;}

int dp[N][2];

void Dp(int x,int y) {
	rep(i,x) if (t[i]!=y) Dp(t[i],x);
	dp[x][0]=dp[x][1]=1;int s0,s1;
	rep(i,x)
		if (t[i]!=y) {
			// 选
			s0=(ll)dp[x][0]*dp[t[i]][1]%Mo*n%Mo;
			s1=(ll)dp[x][1]*dp[t[i]][1]%Mo*n%Mo;
			// 不选
			(s0+=(ll)dp[x][0]*dp[t[i]][0]%Mo*z%Mo)%=Mo;
			(s1+=(ll)dp[x][1]*dp[t[i]][0]%Mo*z%Mo)%=Mo;
			(s1+=(ll)dp[x][0]*dp[t[i]][1]%Mo*z%Mo)%=Mo;
			dp[x][0]=s0;dp[x][1]=s1;
		}
}

void solve_1() {
	fo(i,1,n-1) {
		int x=read(),y=read();
		add(x,y);add(y,x);
	}
	if (y==1) {
		printf("%d\n",pwr(n,n-2));
		return;
	}
	z=pwr(y,Mo-2);z--;
	Dp(1,0);
	int ans=(ll)dp[1][1]*pwr(n,Mo-2)%Mo;
	ans=(ll)ans*pwr(y,n)%Mo;
	printf("%d\n",ans);
}

ll W[2][N];

void init(int b) {
	for(int i=1;i<(1<<b);i<<=1){
		ll wn=pwr(3,(Mo-1)/(i<<1));
		for(int j=0;j<i;++j) W[1][i+j]=(j?wn*W[1][i+j-1]%Mo:1);
		wn=pwr(3,Mo-1-(Mo-1)/(i<<1));
		for(int j=0;j<i;++j) W[0][i+j]=(j?wn*W[0][i+j-1]%Mo:1);
	}
}

void DFT(ll *a,int len,int flag) {
	if (flag==-1) flag=0;
	for(int i=0,j=0;i<len;++i){
		if (i<j) swap(a[i],a[j]);
		for(int k=len>>1;(j^=k)<k;k>>=1);
	}
	for(int i=1;i<len;i<<=1)
		for(int j=0;j<len;j+=(i<<1))
			for(int k=0;k<i;++k) {
				ll x=a[j+k],y=a[j+k+i]*W[flag][i+k]%Mo;
				a[j+k]=(x+y)%Mo;
				a[j+k+i]=(x-y)%Mo;
			}
	ll inv=pwr(len,Mo-2);
	if (!flag) for(int i=0;i<len;i++) a[i]=a[i]*inv%Mo;
}

ll c[N];

void get_Inv(ll *a,ll *b,int n) {
	if (n==1) {b[0]=pwr(a[0],Mo-2);return;}
	get_Inv(a,b,n>>1);
	int len=n<<1;
	fo(i,0,n-1) c[i]=a[i];fo(i,n,len-1) c[i]=0;
	fo(i,(n>>1),len-1) b[i]=0;
	DFT(c,len,1);DFT(b,len,1);
	fo(i,0,len-1) b[i]=(2*b[i]-b[i]*b[i]%Mo*c[i])%Mo;
	DFT(b,len,-1);
	fo(i,n,len-1) b[i]=0;
}

ll f[N],g[N];

void get_ln(ll *a,ll *b,int n) {
	fo(i,0,n-2) f[i]=a[i+1]*(i+1)%Mo;f[n-1]=0;
	get_Inv(a,g,n);
	int len=n<<1;
	fo(i,n,len-1) f[i]=g[i]=0;
	DFT(f,len,1);DFT(g,len,1);
	fo(i,0,len-1) f[i]=f[i]*g[i]%Mo;
	DFT(f,len,-1);
	fo(i,1,n-1) b[i]=f[i-1]*pwr(i,Mo-2)%Mo;
	b[0]=0;
}

ll h[N];

void get_exp(ll *a,ll *b,int n) {
	if (n==1) {b[0]=1;return;}
	get_exp(a,b,n>>1);
	get_ln(b,h,n);
	fo(i,0,n-1) h[i]=(a[i]-h[i]+Mo)%Mo;
	(h[0]=h[0]+1)%=Mo;
	int len=n<<1;
	fo(i,n,len-1) h[i]=b[i]=0;
	DFT(h,len,1);DFT(b,len,1);
	fo(i,0,len-1) b[i]=b[i]*h[i]%Mo;
	DFT(b,len,-1);
	fo(i,n,len-1) b[i]=0;
}

ll F[N],G[N],fac[N],inv[N];

void solve_2() {
	init(18);
	if (y==1) {
		printf("%d\n",pwr(pwr(n,n-2),2));
		return;
	}
	z=pwr(y,Mo-2);int iz=pwr(z-1,Mo-2);
	fac[0]=1;fo(i,1,n) fac[i]=fac[i-1]*i%Mo;
	inv[n]=pwr(fac[n],Mo-2);fd(i,n-1,0) inv[i]=(ll)inv[i+1]*(i+1)%Mo;
	fo(i,1,n) F[i]=(ll)n*n%Mo*iz%Mo*pwr(i,i)%Mo*inv[i]%Mo;
	int len=1;for(;len<=n;len<<=1);get_exp(F,G,len);
	int ans=(ll)G[n]*fac[n]%Mo;
	ans=(ll)ans*pwr(pwr(n,Mo-2),4)%Mo*pwr(z-1,n)%Mo;
	ans=(ll)ans*pwr(y,n)%Mo;
	printf("%d\n",(ans+Mo)%Mo);
}

int main() {
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	n=read();y=read();int op=read();
	if (op==0) solve_0();
	if (op==1) solve_1();
	if (op==2) solve_2();
	return 0;
}
<think>嗯,用户问的是如何在命令行中使用wc -l命令只获取字结果。好的,首先我要回忆一下wc命令的基本用法。wc是word count的缩写,通常用来统计文件的行、字或字节。参-l表示统计行。默认情况下,wc -l会输出行后跟文件名,比如“5 file.txt”。但用户只想要字,不带文件名,特别是当处理单个文件的时候。 那怎么去掉文件名呢?我记得如果wc命令接收的是标准输入而不是文件的话,输出就只会有字而没有文件名。比如用管道把cat的结果传给wc -l,像这样:cat file.txt | wc -l。这时候不管有没有文件名,输出都只有字。不过这个方法可能效率不高,因为用cat其实没有必要,可以直接用输入重定向。例如:wc -l < file.txt,这样会从标准输入读取文件内容,输出行,而且不会有文件名。 然后用户可能有多个文件需要处理,想要每个文件的结果都只显示字。这时候需要遍历文件,对每个单独使用输入重定向。可以用for循环,比如在bash中:for f in *.txt; do echo -n "$f: "; wc -l < "$f"; done。这样每个文件都会显示文件名和对应的行,但行部分只有字。 不过用户的问题可能更侧重于单个文件的情况,所以重点应该放在如何去掉文件名,得到纯字。另外,可能要注意处理带有空格的文件名,这种情况下需要确保变量被正确引用,避免解析错误。例如,在循环中使用"$f"而不是$f。 还有一种情况是,如果用户已经有一个命令输出了内容,想统计行,这时候用管道传递到wc -l就可以了,比如ls | wc -l,统计当前目录下的文件量,结果只有字。 总结一下,主要的方法有两种:一种是使用输入重定向,另一种是通过管道传递内容。这两种方法都能确保wc -l只输出字,不带有文件名或其他信息。需要根据具体情况选择合适的方法,同时注意命令的效率和正确性。</think>要使用 `wc -l` 命令仅获取字结果(不显示文件名),可以通过以下方法实现: --- ### 1. **通过输入重定向** 当直接统计文件时,使用 `<` 将文件内容作为标准输入传递给 `wc -l`: ```bash wc -l < 文件名 ``` **示例**: ```bash wc -l < example.txt ``` **输出**: ``` 42 # 仅显示文件的行 ``` --- ### 2. **通过管道传递内容** 如果内容是其他命令的输出,可以用管道 `|` 将结果传递给 `wc -l`: ```bash cat 文件名 | wc -l ``` 或结合其他命令(如 `grep`): ```bash grep "pattern" 文件名 | wc -l ``` **输出**: ``` 42 # 仅显示行 ``` --- ### 3. **处理多个文件** 若需统计多个文件且仅显示字,可遍历文件并用输入重定向: ```bash for file in *.txt; do echo "$file: $(wc -l < "$file")" done ``` **输出**: ``` file1.txt: 42 file2.txt: 15 ``` --- ### 原理说明 - **`wc -l` 的默认行为**:直接作用于文件时,会输出 `行 文件名`(例如 `42 example.txt`)。 - **输入重定向 `<`**:将文件内容作为标准输入,`wc -l` 不会感知到文件名,因此仅输出字。 - **管道 `|`**:将前一个命令的输出作为标准输入传递给 `wc -l`,同样不会包含文件名。 --- ### 应用场景 - 脚本中需要提取行进行后续计算。 - 统计日志文件、代码行等自动化任务。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值