题目链接:Comet OJ

A.迫真字符串

#include<bits/stdc++.h>
using namespace std;
const int mx = 2e5+7;
int a[20],n;
char s[mx];
int main(){
	scanf("%s",s);
	for(int i=0;s[i];i++)
	a[s[i]-'0']++;
	int ans = min(a[1]/3,a[5]);
	ans = min(ans,a[4]/2);
	printf("%d\n",ans);
	return 0;
}

B.迫真数论

实际上也就是17,18可以。

C.迫真小游戏

对ai排个序,从小到大,然后再将深度从小到大排个序,枚举一个个塞进优先队列中,然后贪心就ok了。赛时没有标记已经被选过的点,一直wa,后来想想没有标记确实有可能出错。

#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pa;
const int mx = 5e5+7;
int n,a[mx],dep[mx],ans[mx],sz;
vector <int> g[mx];
pa b[mx],c[mx];
bool vis[mx];
priority_queue <int,vector<int>,greater<int>> q;
void dfs(int u,int fa,int d){
	dep[u] = d;
	for(int v:g[u]) if(v!=fa)
	dfs(v,u,d+1);
}
int main(){
	scanf("%d",&n);
	int u,v,now;
	for(int i=1;i<n;i++){
		scanf("%d%d",&u,&v);
		g[u].push_back(v);
		g[v].push_back(u);
	}
	dfs(1,0,1);
	for(int i=1;i<=n;i++){
		scanf("%d",&u);
		b[i].fi = u,b[i].se = i;
		c[i].fi = dep[i],c[i].se = i;
	}
	sort(b+1,b+1+n);
	sort(c+1,c+1+n);
	for(int i=1,j=1;i<=n;i++){
		v = b[i].se;
		if(vis[v]) continue;
		while(j<=n&&c[j].fi<=b[i].fi) q.push(c[j].se),j++;
		while(!q.empty()){
			int now = q.top();
			if(now<=v){
				ans[sz++] = now;
				vis[now] = 1;
				q.pop();
			}else break;
		}
	}
	for(int i=0;i<n;i++)
	printf("%d%c",ans[i],i==n-1?'\n':' ');
	return 0;
}

D.迫真图论

对于一个边的权值我们可以考虑把它放到其中一个端点中,对于端点可以采用一个分块,将点的度为<S和>=S两种,设S>=sqrt(m)。

1.若一条边的端点有一个点的度>=S,那么就将这个边的值放到其中一个度较大的当中,但是当另一个端点值被改变时这个边的转化下标也会受到影响,所以对于另一个端点要记录没放在它里面的边。

2.若两个边的度都<S,那么都不放在端点里面考虑,直接用树状数组更新就好了。然后将这个边都记录到两端,因为这个边都没有被放入。

所以对于改变一个节点的权值,只需要去暴力更新该节点记录的没被放在该节点的边。

一个节点最多存下<S个边,因为当这个节点的度<S时,并且跟他相邻的点的度也<S时,因为度>=S的点最多m/S<=sqrt(m)

对于不存放在端点的边的暴力更新,可以采用分块法,那样更新时O(1),查询是sqrt(n),不用树状数组是因为查询对多q次,但是对于更新每个节点如果都是S个边,对于另一端点度<S的边来说,更新时O(1),对于另一端点度>=S来说更新时O(logn),一个节点最多存在度>=S的点是m/S个,所以最坏情况是q*max(sqrt(n),m/S*log(n),S)

最后就是放在端点里的边了,度>=S的点最多是m/S,对于每个点我们可以去做一个树状数组,维护边的另一个端点的值得前缀和个数。对于端点值为b,然后我要查询和b异或<=x的前缀和,这个可以枚举二进制,最多分成log(n)的连续的段,所以可以O(log^2(n))查询。所以对于查询最坏的情况是O(q*m/S*log^2(n)),

所以S的选取也是很关键的,当S取到sqrt(m)*log(m)时,可以使得复杂度更低。时间复杂度可以优化到O(q*sqrt(m)*log(n))

#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pa;
const int N = 131072;
const int mod = 998244353;
const int mx = N+10;
const int B = 400;
const int limt = 6000;
int n,m,Q,b[mx],id[mx];
int du[mx],q[5555],hd;
int be[mx];
ll s[80][mx],s1[mx],s2[mx];
vector <int> g[mx];
struct node{
	int x,y;
	int c;
}e[mx];
void add(int o,int x,int v){
	if(!x) { s[o][0] += v; return ;} 
	for(;x<N;x+=x&-x)
	s[o][x] += v;
}
void add(int x,int v){
	s1[x] += v;
	s2[x/B] += v; 
} 
void upt(int x,int v){
	node rt = e[x];
	if(be[x]){
		int o = be[x];
		add(o,b[rt.x]^b[rt.y]^b[q[o]],v*rt.c);
	}else add(b[rt.x]^b[rt.y],v*rt.c);
}
ll ask(int x){
	ll ans = 0;
	int o = x / B;
	for(int i=0;i<o;i++) ans += s2[i];
	for(int i=x/B*B;i<=x;i++) ans += s1[i];
	return ans;
}
ll ask(int o,int x){
	if(x==-1) return 0;
	ll ans = s[o][0];
	for(;x;x-=x&-x) ans += s[o][x];
	return ans;
}
ll ask(int o,int l,int r){
	return ask(o,r) - ask(o,l-1);
}
ll query(int x){
	if(x==-1) return 0;
	ll ans = ask(x);
	for(int i=1;i<=hd;i++){
		int o = b[q[i]],L = 0;
		for(int j=16;j>=0;j--){
			if(x>>j&1){
				if(o>>j&1)
				ans += ask(i,L+(1<<j),L+(1<<j+1)-1);
				else
				ans += ask(i,L,L+(1<<j)-1),L += (1<<j);
			}else if(o>>j&1) L += (1<<j); 
		}
		ans += ask(i,L,L);
	}
	return ans;
}
int main(){
	scanf("%d%d%d",&n,&m,&Q);
	for(int i=1;i<=n;i++) scanf("%d",b+i);
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].c);
		du[e[i].x]++,du[e[i].y]++;
	}
	for(int i=1;i<=n;i++)if(du[i]>=limt){
		hd++,q[hd] = i;
		id[i] = hd; 
	}
	for(int i=1;i<=m;i++){
		int L  = e[i].x,R = e[i].y;
		if(id[L]){
			be[i] = id[L];
			g[R].push_back(i);
		}else if(id[R]){
			be[i] = id[R];
			g[L].push_back(i);
		}else{
			g[L].push_back(i);
			g[R].push_back(i);	
		}
		upt(i,1);
	}
	int u,v,c;
	for(int i=1;i<=Q;i++){
		scanf("%d%d%d",&c,&u,&v);
		if(c==1){
			for(int j:g[u]) upt(j,-1);
			b[u] = v;
			for(int j:g[u]) upt(j,1);
		}else if(c==2){
			upt(u,-1);
			e[u].c = v;
			upt(u,1);
		}else{
			printf("%lld\n",(query(v)-query(u-1))%mod);
		}
	}
	return 0;
}

E.迫真大游戏

p为一个点消失的概率,那么q = 1-p就是没有消失的概率


Comet OJ - Contest #5 A-E_i++

表示n个点并且是第一个点最后一个消失的概率,我们可以枚举第一圈小时了多少个点,那么有:                                           

Comet OJ - Contest #5 A-E_算法_02

                                            

Comet OJ - Contest #5 A-E_#include_03

这个可以用分治fft求出

Comet OJ - Contest #5 A-E_i++_04

,O(n*log(n)*log(n))另外令

Comet OJ - Contest #5 A-E_算法_05

表示n个点然后第i个点最后一个消失的概率,那么有:                                            

Comet OJ - Contest #5 A-E_#include_06

                                            

Comet OJ - Contest #5 A-E_#include_07

然后这两个还是可以fft,O(n*log(n))

我们再看下求fn能不能优化,将问题转化为n个点,每次每个点都做一次判定,有p的概率被标记,被标记之后下次还可以再标记,请问点被最后一个标记的概率,然后我们枚举这个点在第i+1被标记的概率,那么有:

                                             

Comet OJ - Contest #5 A-E_c++_08

后面二项式展开:

                                             

Comet OJ - Contest #5 A-E_算法_09

                                              

Comet OJ - Contest #5 A-E_#include_10

有当x<1时1+x+x^2+x^3+x^4+...+x^n = 1/(1-x),得

                                              

Comet OJ - Contest #5 A-E_#include_11

然后这个组合数分开,然后就可以用一个fft求出来了。

分治做法:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 998244353;
const int g = 3;
const int len = 19;
const int mx = 1<<len;
int n,x,y,rev[mx];
ll fac[mx],inv[mx],p,q;
ll g1[mx],g2[mx],c[mx];
ll a[mx],f[mx],b[mx];
void get_rev(int len)
{
	for(int i=1;i<(1<<len);i++)
	rev[i] = (rev[i>>1]>>1)|((i&1)<<(len-1));
}
ll qpow(ll x,ll y){
	ll ans = 1;
	while(y){
		if(y&1) ans = ans*x%mod;
		y >>= 1;
		x = x*x%mod;
	}
	return ans;
}
void init(int x,int y)
{
	int _g = __gcd(x,y);
	x /= _g,y /= _g;
	p = x*qpow(y,mod-2)%mod,q = (1-p+mod)%mod;
	g1[0] = g2[0] = 1;
	for(int i=1;i<mx;i++)
	g1[i] = p*g1[i-1]%mod,g2[i] = q*g2[i-1]%mod;
	f[1] = inv[0] = fac[0] = 1;
	inv[1] = fac[1] = 1;
	for(int i=2;i<mx;i++){
		fac[i] = fac[i-1]*i%mod;
		inv[i] = (mod-mod/i)*inv[mod%i]%mod;
	}
	for(int i=2;i<mx;i++) inv[i] = inv[i]*inv[i-1]%mod;
}
void ntt(ll *p,int len,int v)
{
	for(int i=0;i<len;i++)
	if(i<rev[i]) swap(p[i],p[rev[i]]);
	for(int i=1;i<len;i<<=1){
		ll tep = qpow(g,(mod-1)/(2*i));
		if(v<0) tep = qpow(tep,mod-2);
		for(int j=0;j<len;j+=(i<<1)){
			ll mul = 1;
			for(int k=j;k<i+j;k++){
				ll x = p[k];
				ll y = mul*p[k+i]%mod;
				p[k] = (x + y)%mod;
				p[k+i] = (x - y + mod)%mod;
				mul = mul*tep%mod;
			}
		}
	}
	if(v==-1){
		ll invs = qpow(len,mod-2);
		for(int i=0;i<len;i++) p[i] = p[i]*invs%mod;
	}
}
void cdq(int l,int r,int sz){
	if(l==r) return ;
	int mid = l+r>>1,len = (r-l+1);
	cdq(l,mid,sz-1);
	for(int i=0;i<len/2;i++)
	a[i] = g2[i+l]*f[i+l]%mod*inv[i+l-1]%mod;
	for(int i=len/2;i<2*len;i++) a[i] = 0;
	b[0] = 0;
	for(int i=1;i<len;i++) b[i] = g1[i]*inv[i]%mod;
	for(int i=len;i<2*len;i++) b[i] = 0;
	get_rev(sz);
	ntt(a,1<<sz,1);ntt(b,1<<sz,1);
	for(int i=0;i<(1<<sz);i++) a[i] = a[i]*b[i]%mod;
	ntt(a,1<<sz,-1);
	for(int i=mid+1;i<=r;i++) 
	f[i] = (f[i]+c[i]*a[i-l])%mod;
	cdq(mid+1,r,sz-1); 
}
int main(){
	int x,y;
	scanf("%d",&n);
	scanf("%d%d",&x,&y);
	init(x,y);
	for(int i=2;i<=n;i++)
	c[i] = fac[i-1]%mod*qpow((1-qpow(q,i)+mod)%mod,mod-2)%mod;
	cdq(1,1<<(len-1),len);
	for(int i=0;i<(1<<len);i++) {
		if(i<n) a[i] = g1[i]*inv[i]%mod*f[n-i]%mod;
		else a[i] = 0; 
	}
	for(int i=0;i<(1<<len);i++)
	b[i] = i<n?g2[i]*inv[i]%mod:0;
	get_rev(len);
	ntt(a,1<<len,1);ntt(b,1<<len,1);
	for(int i=0;i<(1<<len);i++) a[i] = a[i]*b[i]%mod;
	ntt(a,1<<len,-1);
	for(int i=1;i<=n;i++) printf("%lld\n",a[i-1]*fac[i-1]%mod);
	return 0;
}

一个fft做法:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 998244353;
const int g = 3;
const int len = 19;
const int mx = 1<<len;
int n,x,y,rev[mx];
ll fac[mx],inv[mx],p,q;
ll g1[mx],g2[mx],c[mx];
ll a[mx],f[mx],b[mx];
void get_rev(int len)
{
	for(int i=1;i<(1<<len);i++)
	rev[i] = (rev[i>>1]>>1)|((i&1)<<(len-1));
}
ll qpow(ll x,ll y){
	ll ans = 1;
	while(y){
		if(y&1) ans = ans*x%mod;
		y >>= 1;
		x = x*x%mod;
	}
	return ans;
}
void init(int x,int y)
{
	int _g = __gcd(x,y);
	x /= _g,y /= _g;
	p = x*qpow(y,mod-2)%mod,q = (1-p+mod)%mod;
	g1[0] = g2[0] = 1;
	for(int i=1;i<mx;i++)
	g1[i] = p*g1[i-1]%mod,g2[i] = q*g2[i-1]%mod;
	f[1] = inv[0] = fac[0] = 1;
	inv[1] = fac[1] = 1;
	for(int i=2;i<mx;i++){
		fac[i] = fac[i-1]*i%mod;
		inv[i] = (mod-mod/i)*inv[mod%i]%mod;
	}
	for(int i=2;i<mx;i++) inv[i] = inv[i]*inv[i-1]%mod;
}
void ntt(ll *p,int len,int v)
{
	for(int i=0;i<len;i++)
	if(i<rev[i]) swap(p[i],p[rev[i]]);
	for(int i=1;i<len;i<<=1){
		ll tep = qpow(g,(mod-1)/(2*i));
		if(v<0) tep = qpow(tep,mod-2);
		for(int j=0;j<len;j+=(i<<1)){
			ll mul = 1;
			for(int k=j;k<i+j;k++){
				ll x = p[k];
				ll y = mul*p[k+i]%mod;
				p[k] = (x + y)%mod;
				p[k+i] = (x - y + mod)%mod;
				mul = mul*tep%mod;
			}
		}
	}
	if(v==-1){
		ll invs = qpow(len,mod-2);
		for(int i=0;i<len;i++) p[i] = p[i]*invs%mod;
	}
}
void solve(int len){
	get_rev(len);
	ntt(a,1<<len,1);ntt(b,1<<len,1);
	for(int i=0;i<(1<<len);i++) a[i] = a[i]*b[i]%mod;
	ntt(a,1<<len,-1);
}
int main(){
	int x,y;
	scanf("%d",&n);
	scanf("%d%d",&x,&y);
	init(x,y);
	for(int i=0;i<(1<<len);i++){
		if(i<n){
			a[i] = inv[i]*qpow(1-qpow(q,i+1)+mod,mod-2)%mod;
			if(i&1) a[i] = mod - a[i];
		}else a[i] = 0;
	}
	for(int i=0;i<(1<<len);i++)
	b[i] = i<n?inv[i]:0;
	solve(len);
	for(int i=1;i<=n;i++) f[i] = a[i-1]*fac[i-1]%mod*p%mod; 
	for(int i=0;i<(1<<len);i++)
	a[i] = i<n?g1[i]*inv[i]%mod*f[n-i]%mod:0;
	for(int i=0;i<(1<<len);i++)
	b[i] = i<n?g2[i]*inv[i]%mod:0;
	solve(len);
	for(int i=1;i<=n;i++) printf("%lld\n",a[i-1]*fac[i-1]%mod);
	return 0;
}