abc247题解(A-Ex)

A

题意简述

给定一个字符串 \(S\)\(\text{0}\)\(\text{1}\) 组成且长度为 \(4\)。将 \(S\) 全部向右移一位,舍弃掉最后一位,第一位补 \(\text{0}\)。输出操作后的字符串。

题解

模拟即可。

#include <bits/stdc++.h>
using namespace std;
string s;
signed main() {
	cin>>s;
	s[3]=s[2],s[2]=s[1],s[1]=s[0],s[0]='0';
	cout<<s;
	return 0;
}

B

题意简述

\(n\) 个人,每个人各有一个姓和名为 \(s_i\)\(t_i\)。现在要给每一个人起一个昵称,第i个人的昵称从 \(s_i\)\(t_i\) 中选择。问存不存在至少一个方案使得所有人的昵称不重复。
\(1<=n<=100\)

题解

如果存在一个人 \(i\),无论他选 \(s_i\) 还是 \(t_i\) 都与其他人有重复就没有合法方案。

所以 \(n^2\) 枚举检查即可。

#include <bits/stdc++.h>
using namespace std;
const int N=105;
int n;
string s[N],t[N];
signed main() {
	cin>>n;
	for(int i=1;i<=n;++i) cin>>s[i]>>t[i];
	for(int i=1;i<=n;++i) {
		int flag=0;
		for(int j=1;j<=n;++j)
			if(i!=j&&(s[i]==s[j]||s[i]==t[j])) {
				++flag;
				break;
			}
		for(int j=1;j<=n;++j)
			if(i!=j&&(t[i]==s[j]||t[i]==t[j])) {
				++flag;
				break;
			}
		if(flag==2) puts("No"),exit(0);
	}
	puts("Yes");
	return 0;
}

C

题意简述

定义序列 \(s_n\) 如下:
1.如果 \(n=1\)\(s_{n}=\text{1}\)
2.如果 \(n>1\)\(s_{n}\)\(s_{n-1},n,s_{n-1}\)
比如:\(s_2\)\(\text{1,2,1}\);\(s_3\)\(\text{1,2,1,3,2,1}\)
现在给定 \(n\),求 \(s_n\)
\(n<=16\)

题解

递推,用字符串实现。

#include <bits/stdc++.h>
using namespace std;
const int N=18;
int n;
string s[N],d[N]={"0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16"};
signed main() {
	cin>>n;
	s[1]="1";
	for(int i=2;i<=n;i++) s[i]=s[i-1]+" "+d[i]+" "+s[i-1]; 
	cout<<s[n];
	return 0;
}

D

题意简述

给定 \(q\) 个操作。有两种类型。
(1) 1 x c:在队列右边插入 \(c\) 个权值为 \(x\) 的球。
(2) 2 c:取出队头的 \(c\) 个球,并输出这些球的权值和。
\(1<=q<=200000,0<=x<=10^9,1<=c<=10^9\)

题解

还是模拟,搞一个队列放加入的数,把所有相同的数压在一起,从队头开始依次取出。
把相同颜色的球压在一起。

#include <bits/stdc++.h>
#define int long long
using namespace std;
int read() {
	int x(0),f(0);
	char ch=getchar();
	while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
const int N=200005;
int q,a[N],c[N],n,st=1;
signed main() {
	q=read();
	while(q--) {
		int op=read();
		if(op==1) a[++n]=read(),c[n]=read();
		else {
			int b=read(),tot=0,sum=0;
			while(st<=n&&c[st]+tot<=b) tot+=c[st],sum+=a[st]*c[st],++st;
			c[st]-=b-tot,sum+=(b-tot)*a[st];
			cout<<sum<<"\n";
		}
	}
	return 0;
}

E

题意简述

有一个长度为n的正整数序列 \(a_i\),和正整数 \(x,y(x>=y)\)。求满足以下条件的整数对 \((l,r)\) 的数量。
(1)\(1<=l<=r<=n\)
(2)\(\max_{i=l}^{r}{(a_i)}=x\),\(\min_{i=1}^{r}{(a_i)}=y\)
\(1<=n,a_i,l,r<=200000\)

题解

求出满足区间内所有 \(a_i<=x\)\(a_i>=y\) 的区间的数量,减去满足区间内所有 \(a_i<=x-1\)\(a_i>=y\) 的区间的数量和满足区间内所有 \(a_i<=x\)\(a_i>=y+1\) 的区间的数量,再加上满足区间内所有 \(a_i<=x-1\)\(a_i>=y+1\) 的区间的数量就是答案。
\(O(n)\) 扫一遍就可以求一种。

#include <bits/stdc++.h> 
#define int long long
using namespace std;
int read() {
	int x(0),f(0);
	char ch=getchar();
	while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
const int N=200005;
int n,a[N];
int calc(int x,int y) {
	int res=0;
	for(int i=1,j=0;i<=n;++i) {
		if(a[i]>x||a[i]<y) j=i;
		res+=i-j;
	}
	return res;
}
signed main() {
	n=read();
	int x=read(),y=read();
	for(int i=1;i<=n;++i) a[i]=read();
	cout<<calc(x,y)-calc(x-1,y)-calc(x,y+1)+calc(x-1,y+1);
	return 0;
}

F

题意简述

\(n\) 张卡牌,第 \(i\) 张卡牌的正面写着 \(p_i\),背面写着 \(q_i\)\(p\)\(q\)\((1,2,...,n)\) 的一个排列。现在要选出一些卡牌,使得这些卡牌的正面和背面的数字包含了 \(1,2,...,n\) 中的每一个数。求方案数对 \(998244353\) 取模的值。
\(1<=n<=200000\)

题解

显然卡牌的初始顺序不重要,所以我们将卡牌按照 \(p_i\) 从小到大排序。可以将这些关系转换转换为有 \(n\) 个点,\(n\) 条边的有向图,\(i\)\(q_i\) 连边。我们发现在这个有向图中有若干个环,且这些环之间相互独立,所以对于每个环算方案数,全部乘起来就是答案。显然每个环内的方案数只与环的大小有关。这个子问题可以 DP 求解。状态为 \(f_{i,0/1,0/1}\) 表示当前选到了第 \(i\) 个,第 \(i\) 个选/不选,环头选/不选。转移显然。设大小为 \(i\) 的环的方案数为 \(g_i\),由上面的 DP 可推出 \(g_1=1\),\(g_2=3\),\(g_i=g_{i-1}+g_{i-2}(i>=3)\)。所以只需要算这个斐波那契就可以了。

#include <bits/stdc++.h>
#define int long long
using namespace std;
int read() {
	int x(0),f(0);
	char ch=getchar();
	while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
const int N=200005,mod=998244353;
int n,p[N],q[N],a[N],f[N],ans=1;
bool vis[N];
signed main() {
	n=read();
	for(int i=1;i<=n;++i) p[i]=read();
	for(int i=1;i<=n;++i) q[i]=read(),a[q[i]]=i;
	f[1]=1,f[2]=3;
	for(int i=3;i<=n;++i) f[i]=(f[i-1]+f[i-2])%mod;
	for(int i=1;i<=n;++i) {
		if(vis[i]) continue;
		int j=i,t=0;
		while(1) {
			++t;
			vis[j]=1;
			j=a[p[j]];
			if(vis[j]) break;
		}
		ans=ans*f[t]%mod;
	}
	cout<<ans;
	return 0;
}

G

题意简述

\(n\) 个程序员。第 \(i\) 个程序员来自大学 \(a_i\),擅长技能 \(b_i\),能力为\(c_i\)
一个队伍由 \(n\) 个人中的一些人组成。我们把满足以下条件的队伍称为梦之队:
1.队中任意两个人都属于不同的两个大学。
2.队中任意两个人都擅长不同的两个学科。
定义 \(k\) 为一个梦之队能包含的的最多的人数。对于每个 \(i=1,2,...,k\),解决以下问题:求一个人数为 \(i\) 的梦之队的最大的所有人的能力的和。
\(1<=n<=30000,1<=a_i,b_i<=150,1<=c_i<=10^9\)

题解

很显然的二分图带权匹配。跑费用流。将源点向每个大学连边,每个技能向汇点连边,流量为 \(1\),费用为 \(0\)。对于每一个程序员,将 \(a_i\)\(b_i\) 连边,流量为 \(1\),费用为 \(-c_i\),因为要跑最小费用最大流。不断将源点的流量设为一跑费用流即可。设 \(max(a_i,b_i)\)\(m\),时间复杂度为\(O(m(m+n)log(m+n))\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
int read() {
	int x(0),f(0);
	char ch=getchar();
	while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
const int N=505,M=100005;
int n,s,t,cur=1,hed[N],to[M],nxt[M],w[M],c[M],dis[N],now[N];
void add(int u,int v,int x,int y) {
	nxt[++cur]=hed[u],to[cur]=v,w[cur]=x,c[cur]=y,hed[u]=cur;
	nxt[++cur]=hed[v],to[cur]=u,w[cur]=0,c[cur]=-y,hed[v]=cur;
}
queue<int> qu;
bool vis[N];
bool spfa() {
	memset(dis,0x3f,sizeof dis);
	qu.push(s),dis[s]=0;
	while(!qu.empty()) {
		int x=qu.front();
		qu.pop();
		vis[x]=0,now[x]=hed[x];
		for(int i=hed[x];i;i=nxt[i]) {
			int y=to[i],z=c[i];
			if(w[i]&&dis[y]>dis[x]+z) {
				dis[y]=dis[x]+z;
				if(!vis[y]) vis[y]=1,qu.push(y);
			}
		}
	}
	return dis[t]<dis[0];
}
int res;
int dfs(int x,int flow) {
	if(x==t) return flow;
	int rest=flow;
	vis[x]=1;
	for(int i=now[x],y;i&&rest;i=nxt[i]) {
		now[x]=i,y=to[i];
		if(!vis[y]&&w[i]&&dis[y]==dis[x]+c[i]) {
			int p=dfs(y,min(rest,w[i]));
			if(!p) dis[y]=-1;
			w[i]-=p,w[i^1]+=p,rest-=p,res+=p*c[i];
		}
	}
	vis[x]=0;
	return flow-rest;
}
int ans[N],ma;
signed main() {
	n=read();
	s=301,t=302;
	for(int i=1;i<=150;++i) add(s,i,1,0),add(i+150,t,1,0);
	for(int i=1;i<=n;++i) {
		int a=read(),b=read(),c=read();
		add(a,b+150,1,-c);
	}
	while(spfa()) {
		dfs(s,1);
		ans[++ma]=-res;
	}
	cout<<ma<<"\n";
	for(int i=1;i<=ma;++i) cout<<ans[i]<<"\n";
	return 0;
}

Ex

题意简述

\(n\) 个人,第 \(i\) 个人穿着颜色为 \(c_i\) 的衣服。高木要执行 \(k\) 次操作,操作的内容是:选择两个人交换他们的位置。经过 \(k\) 次操作后,之后第 \(i\) 个人的衣服的颜色还是 \(c_i\)。求可能的最终的人的排列的方案数对 \(998244353\) 取模的值。

题解

排列问题,考虑置换环。发现最终的排列中只在相同的 \(c_i\) 形成的连通块内部做交换,即排列的每个置换环内 \(c_i\) 要相同。
引理 1:任意交换两个数,置换环的个数会 \(+1/-1\)
显然,如果交换两个原本在同一置换环内的数,这个置换环会分裂成两个置换环;否则会把两个置换环合并成一个。
引理 2:设初始排列开始操作 \(k\) 次,得到了一个置换环个数为 \(c\) 的排列,满足 \(n-c\le k\)\(n-c \equiv k \pmod 2\)
因为初始排列的置换环个数为n,通过引理 1 可推得此结论。
所以我们要做的就是对于每一个合法的置换环个数 \(c\),求出合法的排列个数。
考虑 DP,设 \(f[i][j]\) 表示考虑 \([1,i]\) 的排列,置换环个数为j的合法排列数。两种转移:\(i\) 自己为一个新环和把 \(i\) 塞入某一个老环。

\[f[i][j]=f[i-1][j-1]+f[i-1][j]* | {x|1<=x<i,c_i=c_x}| \]

答案就为所有合法的 \(j\)\(f[n][j]\) 的和。
分治卷积优化优化就可以做到 \(O(n \log^2 n)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned int uint;
const int MR=2e5+10,MAXN=2e5+10;
const LL P=998244353,g=3;
int n,k,c[MR],t[MR],cnt[MR];
LL ww[101];
LL* e=ww+50;
LL qpow(LL a, LL k, LL p) {
	LL c=1;
	while(k) {
		if(k&1) c=(c*a)%p;
		k>>=1;
		a=(a*a) % p;
	}
	return c;
}
void primeroot(LL* e) {
	int s=0;
	LL q=P-1;
	while((q&1)==0) s++,q>>=1;
	LL w=qpow(g,q,P);
	LL invw=qpow(w,P-2,P);
	for(int h=s; h>=0; h--) {
		e[h]=w;
		e[-h]=invw;
		w=(w*w)%P;
		invw=(invw*invw)%P;
	}
}
void ntt(LL* f, const int& h, const int& type) {
	if(h==0) return;
	LL f0[1<<(h-1)],f1[1<<(h-1)];
	int n=1<<h;
	for(int i=0; i<n; i+=2) f0[i/2]=f[i];
	ntt(f0,h-1,type);
	for(int i=1; i<n; i+=2) f1[i/2]=f[i];
	ntt(f1,h-1,type);
	LL w=e[type*h],x=1;
	for (int k=0; k<n/2; k++) {
		f[k]=f0[k]+x*f1[k];
		f[k]%=P;
		f[k+n/2]=f0[k]-x*f1[k];
		f[k+n/2]%=P;
		x*=w;
		x%=P;
	}
}
vector<LL> f[MAXN];
LL F1[MAXN*4],F2[MAXN*4];
void mul(int i1,int i2) {
	int limit=f[i1].size()+f[i2].size()-1;
	int deg=1,h=0;
	while(deg<limit) {
		deg<<=1;
		h++;
	}
	for(uint i=0; i<f[i1].size(); i++) F1[i]=f[i1][i];
	for(int i=f[i1].size(); i<deg; i++) F1[i]=0;
	for(uint i=0; i<f[i2].size(); i++) F2[i]=f[i2][i];
	for(int i=f[i2].size(); i<deg; i++) F2[i]=0;
	ntt(F1,h,1);
	ntt(F2,h,1);
	for(int i=0; i<deg; i++) F1[i]=F1[i]*F2[i]%P;
	ntt(F1,h,-1);
	LL inv = qpow(deg, P - 2, P);
	for (int i = 0; i < deg; i++) {
		F1[i] = ((F1[i] * inv) % P + P)%P;
	}
	f[i1].clear();
	for(int i=0; i<limit; i++) f[i1].push_back(F1[i]);
}

int main() {
	primeroot(e);
	scanf("%d%d",&n,&k);
	for(int i=0; i<n; i++) {
		scanf("%d",c+i);
		cnt[i]=t[c[i]];
		t[c[i]]++;
		f[i].push_back(cnt[i]);
		f[i].push_back(1);
	}
	int tmp=n;
	while(n>1) {
		for(int i=0; i<n/2; i++) mul(i+(n&1),i+(n&1)+n/2);
		n=(n+1)/2;
	}
	n=tmp;
	LL ans=0;
	for(int j=max(0,n-k); j<=n; j++) {
		if((j+n-k)&1) continue;
		ans+=f[0][j];
		ans%=P;
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2022-09-12 16:04  lmgoat  阅读(83)  评论(0)    收藏  举报