[考试总结]noip模拟13

因为最近考试频繁,所以咕掉了好长时间。。。

淦,刚说完又来一场。。。

先咕了,等以后有时间再写。。。。

回来了。。。

首先看到这个题目们,感觉就不存好意。。。

然后开始开 \(T1\)

只能蒻蒻地按照题目码一个 \(\mathcal O(n^2)\) 递推。

然后就开始疯狂暴力。。。

T1:

正解实际上是把这个方程还原。

然后可以发现这个式子的贡献可以使用组合数来计算。

然后其实就很简单了。

#include<bits/stdc++.h>
using std::cout; using std::endl;
#define debug cout<<"debug"<<endl
#define int long long
namespace xin_io
{
	#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1++
	#define scanf eat1 = scanf
	#define freopen eat2 = freopen
	int eat1; FILE *eat2; char buf[1<<20],*p1 = buf,*p2 = buf;
	inline void openfile() {freopen("t.txt","r",stdin);} inline void outfile() {freopen("o.txt","w",stdout);}
	template<class type>inline type get()
	{
		type s = 0,f = 1; register char ch = gc();
		while(!isdigit(ch)) {if(ch == '-') f = -1; ch = gc();}
		while(isdigit(ch))  {s = s * 10 + ch - '0'; ch = gc();}
		return s * f;
	}
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 0x7f7f7f7f,mod = 998244353;
#define try(i,a,b) for(register signed i=a;i<=b;++i)
#define throw(i,a,b) for(register signed i=a;i>=b;--i)
typedef long long ll;
namespace xin
{
	int ans = 0,inv[maxn];
	int f[maxn],g[maxn],fac[maxn];
	int n,m,a,b;
	inline void do_pre()
	{
		inv[1] = 1;
		try(i,2,std::max(n,m)<<1) inv[i] = (mod - mod/i) * inv[mod % i] % mod;
		fac[0] = fac[1] = 1;
		try(i,2,std::max(n,m)<<1) fac[i] = fac[i - 1] * i % mod;
	}
	inline int ksm(int x,int y)
	{
		register int ret = 1;
		while(y)
		{
			if(y & 1) ret = ret * x % mod;
			x = x * x % mod; y >>= 1;
		}
		return ret % mod;
	}
	inline int C(int n,int m)
	{
		if(n == 0 or m == 0) return 1;
		return fac[n] * ksm(fac[m] * fac[n-m] % mod,mod-2) % mod;
	}
	int af[maxn],bf[maxn];
	inline short main()
	{
	#ifndef ONLINE_JUDGE
		openfile();
	#endif 
		n = get<ll>(); m = get<ll>(); a = get<ll>() % mod; b = get<ll>() % mod;
		try(i,1,n) f[i] = get<ll>() % mod;
		try(i,1,m) g[i] = get<ll>() % mod;
		do_pre();
		af[0] = bf[0] = 1;
		try(i,1,n<<1) bf[i] = b * bf[i-1] % mod; 
		try(i,1,m<<1) af[i] = a * af[i-1] % mod;
		try(i,0,0)
			try(j,1,m) 
				ans += g[j] % mod * C(n-i+m-j-1,m-j) % mod * af[m-j] % mod * bf[n-i] % mod,ans %= mod; 
		try(j,0,0)
			try(i,1,n)
				ans += f[i] % mod * C(n-i+m-j-1,n-i) % mod * af[m-j] % mod * bf[n-i] % mod,ans %= mod;
		cout<<ans<<endl;
		return 0;
	}
}
signed main() {return xin::main();}

T2:

对于这一个题目,我们首先打一个爆搜。。。

然后我就打错了。。。

好废啊。。。

然而还有一个部分分数。

然后就能愉快 \(40pts\)

但是我废到只能拿到 \(0pts\)

正解实际上是一个树形 \(dp\)

但是我们发现这个万一不是树

但是是一个基环树

所以我们找到环,然后断开一条边就能让他成为一棵树。

所以我们从断开的两个端点开始进行树形 \(dp\)

然后取两次结果的最小值就是最后的答案。

#include<bits/stdc++.h>
using std::cout; using std::endl;
#define debug cout<<"debug"<<endl
namespace xin_io
{
	#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1++
	#define scanf eat1 = scanf
	#define freopen eat2 = freopen
	int eat1; FILE *eat2; char buf[1<<20],*p1 = buf,*p2 = buf;
	inline void openfile() {freopen("t.txt","r",stdin);} inline void outfile() {freopen("o.txt","w",stdout);}
	template<class type>inline type get()
	{
		type s = 0,f = 1; register char ch = gc();
		while(!isdigit(ch)) {if(ch == '-') f = -1; ch = gc();}
		while(isdigit(ch))  {s = s * 10 + ch - '0'; ch = gc();}
		return s * f;
	}
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 0x7f7f7f7f;
#define try(i,a,b) for(register signed i=a;i<=b;++i)
#define throw(i,a,b) for(register signed i=a;i>=b;--i)
typedef long long ll;
namespace xin
{	
	class xin_edge{public:int next,ver;}edge[maxn<<1];
	int head[maxn],zhi = 1;
	inline void add(int x,int y) {edge[++zhi].ver = y; edge[zhi].next = head[x]; head[x] = zhi;}
	int vala,valb,n;
	int f1[maxn][3],f2[maxn][3],w[maxn];
	bool vis[maxn];
	class xin_data
	{
		public:
			int i,l,r;
			xin_data(){}
			xin_data(int i,int l,int r):i(i),l(l),r(r){}
	}root;
	void dfs(int x,int fa)
	{
		vis[x] = 1;
		for(register int i=head[x];i;i=edge[i].next)
		{
			register int y = edge[i].ver;
			if(fa == y) continue;
			if(vis[y]) {root = xin_data(i,x,y);return;}
			dfs(y,x);
		}
	}
	void dp1(int x,int fa)
	{
		register int ret = 0;
		f1[x][1] = w[x]; f1[x][0] = 0;
		for(register int i=head[x];i;i=edge[i].next)
		{
			register int y = edge[i].ver;
			if(y == fa or root.i == i or ((root.i xor 1) == i)) continue;
			dp1(y,x);
			ret += std::min(f1[y][1],f1[y][0]);
			f1[x][0] += f1[y][1];
		}
		f1[x][1] += ret;
	}
	void dp2(int x,int fa)
	{
		register int ret = 0;
		f2[x][1] = w[x]; f2[x][0] = 0;
		for(register int i=head[x];i;i=edge[i].next)
		{
			register int y = edge[i].ver;
			if(y == fa or root.i == i or ((root.i xor 1) == i)) continue;
			dp2(y,x);
			ret += std::min(f2[y][1],f2[y][0]);
			f2[x][0] += f2[y][1];
		}
		f2[x][1] += ret;
	}
	inline short main()
	{
	#ifndef ONLINE_JUDGE
		openfile();
	#endif
		n = get<signed>(); vala = get<signed>(); valb = get<signed>();
		try(i,1,n)
		{
			register int x = get<signed>(),y = get<signed>();
			w[x] += vala; w[y] += valb;
			add(x,y); add(y,x);
		}
		dfs(1,0); 
		dp1(root.l,0);
		dp2(root.r,0);
		cout<<std::min(f1[root.l][1],f2[root.r][1])<<endl;
		return 0;
	}
}
signed main() {return xin::main();}

T3:

这个就是要求:

\[\sum_{i=1}^n(-1)^{\sum_{j=1}^md(i*j)} \]

的值。

然后我们发现这个 \(1\) 的正负只和 \(\sum_{j=1}^md(i*j)\) 有关

所以我们只需要知道这个东西的奇偶性就可以了

我们发现对于一个数,他的约数中如果含有完全平方数,那么它的约数个数就不是奇数,所以我们就可以开始筛出完全平方数的数量。

我们对于 \(i\) 可以将其拆成 \(p*x^2\) 的形式,然后 \(j\) 也可以拆成 \(p*y^2\) 的形式。

我们只需要线性筛出即可。

posted @ 2021-07-15 14:21  NP2Z  阅读(26)  评论(0编辑  收藏  举报