21牛客多校第十场

A

先不考虑空间的限制,考虑每次一个字符串转变的情况,需要找到\(trie\)树上第一个独立于剩余字符串的前缀

则这个前缀即为这次所需的前缀,但需要考虑这个前缀可以替代之前的前缀的情况

新开一个\(num\)数组记录\(trie\)树上每个节点在前缀中出现了几次,则每次新增答案为找到节点的\(1-num[x]\)

再向上更新链上所有点的\(num\)即可

在空间限制下只需要将\(trie\)树的边压缩,在边上存储字符串,加入新字符串的时候考虑分裂一条边的情况

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 250010
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-(b))%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,fst[MAXN],tot=1,rt=1,cnt,sum[MAXN],num[MAXN],ans;
char ch[MAXN][105];
struct Edge{int nxt,to,len;char s[105];}e[MAXN];
void add(int u,int v,char *s,int len)
{
	e[++cnt].nxt=fst[u],fst[u]=cnt,e[cnt].to=v;
	rep(i,0,len-1) e[cnt].s[i]=s[i];e[cnt].len=len;
}
void ins(int p,int cur,int len,int id)
{
	sum[p]++;static int pos,v,lasl;
	for(int i=fst[p];i;i=e[i].nxt) if(e[i].s[0]==ch[id][cur])
	{
		pos=-1;rep(j,0,min(len-cur-1,e[i].len-1))
			if(e[i].s[j]!=ch[id][cur+j]) {pos=j;break;}
		if(pos<0) {ins(e[i].to,cur+e[i].len,len,id);return ;}
		v=e[i].to,lasl=e[i].len;e[i].len=pos;
		e[i].to=++tot;sum[tot]=sum[v];
		add(tot,v,e[i].s+pos,lasl-pos);
		ins(tot,cur+pos,len,id);return ;
	}
	v=++tot;sum[v]++;add(p,v,ch[id]+cur,len-cur);
}
int query(int p,int cur,int len,int id)
{
	if(p!=rt) sum[p]--;if(!sum[p]) {return 1-num[p];}
	for(int i=fst[p],res;i;i=e[i].nxt) if(e[i].s[0]==ch[id][cur])
		{res=query(e[i].to,cur+e[i].len,len,id);num[p]+=res;return res;}
}
int main()
{
	n=read();int len;
	rep(i,1,n) {scanf("%s",ch[i]);len=strlen(ch[i]);ins(rt,0,len,i);}
	rep(i,1,n)
	{
		len=strlen(ch[i]);ans+=query(rt,0,len,i);
		printf("%d\n",ans);
	}
}

B

好像锅了 跑了

C

优化匈牙利 咕了

D

可做dp 咕了

E

大模拟 咕了

F

可以将括号序列转化为一棵树,则题目要求为每个点和它的所有兄弟颜色不同

贪心的分配颜色,将每个点按照儿子个数降序排序,每次选出可用颜色中前\(k\)大的进行分配

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 1001001
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-(b))%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,st[MAXN],tp,num[MAXN],cur,g[MAXN],ans[MAXN];
char s[MAXN<<1];
vector<int> vec[MAXN],tmp;
struct Data{int w,id;};
bool operator < (Data a,Data b){return a.w<b.w;}
priority_queue<Data> q;
bool cmp(int x,int y){return vec[x].size()>vec[y].size();}
int main()
{
	n=read();scanf("%s",s+1);
	rep(i,1,n<<1)
		if(s[i]=='(') vec[st[tp]].pb(++cur),st[++tp]=cur;
		else tp--;
	rep(i,1,n) num[read()]++,g[i]=i;
	rep(i,1,n) if(num[i]) q.push({num[i],i});
	sort(g+1,g+n+1,cmp);int col;
	rep(i,0,n) if(vec[i].size())
	{
		for(auto x:vec[i])
		{
			if(q.empty()) {puts("NO");return 0;}
			col=q.top().id;q.pop();
			ans[x]=col,num[col]--,tmp.pb(col);
		}
		for(auto x:tmp) if(num[x]) q.push({num[x],x});
		tmp.clear();
	}
	puts("YES");
	rep(i,1,n) printf("%d%c",ans[i],i==n?'\n':' ');
}

G

\(g(S)\)表示实际射中人的集合为\(S\)子集的概率,每个人只能选择射不中或者射到集合内的人

\(g(S)=(1-p+\frac{|S|-1}{n-1}p)^{|S|}(1-p+\frac{|S|}{n-1}p)^{n-|S|}=(1-\frac{n-|S|}{n-1}p)^{|S|}(1-\frac{n-|S|-1}{n-1}p)^{n-|S|}\)

可知当集合大小相同时答案相等即\(g(|S|)=g(S)\)

\(f(S)\)为射中人集合恰好为\(S\)的概率,由容斥可得\(f(S)=\sum\limits_{T\subseteq S}(-1)^{|S|-|T|}g(T)=\sum\limits_{i=0}^{|S|}(-1)^{|S|-i}\binom{|S|}{i}g(i)\)

化为卷积形式即\(f(i)=i!\sum\limits_{j=0}^i\frac{(-1)^{i-j}}{(i-j)!}\cdot \frac{g(j)}{j!}\)

\(ans_i=\binom{n}{i}f(i)=\frac{n!}{(n-i)!}\sum\limits_{j=0}^i\frac{(-1)^{i-j}}{(i-j)!}\cdot \frac{g(j)}{j!}\)

得到\(g(i)\)之后卷积即可

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 500100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-(b)+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,p,f[MAXN<<1],g[MAXN<<1],fac[MAXN],ifac[MAXN];
int pw[30],rev[MAXN<<1],ipw[30];
int qp(int x,int t,int res=1)
{
    for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);
    return res;
}
#define inv(x) qp(x,MOD-2)
int mem(int n)
{
    int lg=1,lim=1;
    for(;lim<n;lim<<=1,lg++)
        pw[lg]=qp(3,(MOD-1)/(1<<lg)),ipw[lg]=inv(pw[lg]);
    rep(i,0,lim-1) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-2));
    return lim;
}
void ntt(int *a,int n,int f)
{
    rep(i,0,n-1) if(i<rev[i]) swap(a[i],a[rev[i]]);
    for(int i=1,t=1;i<n;i<<=1,++t)
    {
        int wn= f>0?pw[t]:ipw[t];for(int j=0;j<n;j+=i<<1)
        {
            int w=1,x,y;for(int k=0;k<i;++k,w=mul(w,wn))
                x=a[j+k],y=mul(a[j+k+i],w),a[j+k]=pls(x,y),a[j+k+i]=mns(x,y);
        }
    }
    rep(i,0,n-1) a[i]=pls(a[i],MOD);if(f>0) return ;
    int nv=inv(n);rep(i,0,n-1) a[i]=mul(a[i],nv);
}
int main()
{
	n=read(),p=read(),p=mul(p,inv(read()));
	fac[0]=1;rep(i,1,n) fac[i]=mul(fac[i-1],i);
	ifac[n]=inv(fac[n]);dwn(i,n-1,0) ifac[i]=mul(ifac[i+1],i+1);
	f[0]=1;rep(i,1,n) f[i]=(i&1)?mns(0,ifac[i]):ifac[i];
	rep(i,0,n)
		g[i]=mul(qp(mns(1,qp(n-1,MOD-2,mul(n-i,p))),i),ifac[i]),
		g[i]=mul(qp(mns(1,qp(n-1,MOD-2,mul(n-i-1+MOD,p))),n-i),g[i]);
	int lim=mem(n+1<<1);
	ntt(f,lim,1);ntt(g,lim,1);rep(i,0,lim-1) f[i]=mul(f[i],g[i]);
	ntt(f,lim,-1);
	dwn(i,n,0) printf("%d\n",mul(mul(fac[n],ifac[n-i]),f[i]));
}

H

按照二进制\(1\)的个数奇偶分类后图为二分图,直接染色即可

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-(b))%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n;
int main()
{
	n=read();
	rep(i,0,((1<<n)-1)) putchar(__builtin_popcount(i)&1?'0':'1');
}

I

奇妙构造 咕了

J

板子题 求凸包切线+环的最小区间覆盖 咕

K

有点乱搞 咕

posted @ 2021-08-16 21:25  jack_yyc  阅读(53)  评论(0编辑  收藏  举报