[Comet OJ - Contest #11]简要题解?

前言

md又是rk4_(:з」∠)_
可惜了要是ilnil过了E我们机房就可以加一个冰箱了
题目链接

eon

模拟

Code

#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--)
using namespace std;

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;

char st[N];

int main() {
	scanf("%s",st+1);int n=strlen(st+1);
	int mn=10;
	fo(i,1,n) mn=min(mn,st[i]-'0');
	mn-=(st[n]-'0');
	printf("%d\n",(mn+10)%10);
	return 0;
}

usiness

Dp

Code

#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--)
using namespace std;

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=105,K=2e3+5,inf=0x7fffffff;

int f[K],g[K],n,m,k,mx,val[K],a[N],b[N];

int main() {
	n=read();m=read();k=read();
	fo(i,0,k) val[i]=read(),mx=max(mx,val[i]);
	fo(i,1,m) a[i]=read(),b[i]=read();
	fo(j,0,k+mx) f[j]=-inf;
	f[0]=0;
	fo(i,1,n) {
		fo(w,1,m) fd(j,k+mx,a[w]) f[j-a[w]]=max(f[j-a[w]],f[j]+b[w]); 
		fo(j,0,k+mx) g[j]=-inf;
		fo(j,0,k+mx) g[j+val[j]]=max(g[j+val[j]],f[j]);
		fo(j,0,k+mx) f[j]=g[j];
	}
	int ans=-inf;
	fo(i,0,k+mx) ans=max(ans,f[i]+i);
	printf("%d\n",ans);
	return 0;
}

elebration

一开始觉得好麻烦啊就跳了
后来看发现由于性质优美不需要讨论

考虑a+b+c=n,a,b,c能够成三角形的充要条件为a,b,c<n+12a,b,c\lt{\lfloor{{n+1}\over 2}\rfloor}
预处理出对于每个i,往左右最长能延伸多长

考虑枚举第一段的开头,设为i,那么第二段开头j会满足i<jR[i]+1i\lt j\le R[i]+1
第三段开头k会满足j<kR[j]+1j\lt k\le R[j]+1,且kL[i+n1]1k\ge L[i+n-1]-1
枚举了i和j,那么合法的k的数量是L[i+n1]R[j]+2L[i+n-1]-R[j]+2
由于R是单调递增的,可以用双指针维护合法的j的取值区间
剩下的用一个R的前缀和计数即可
复杂度O(n)O(n)

Code

#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--)
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=4e6+5;

int n,m,a[N],lst[N],L[N],R[N];
ll s[N];

int main() {
	n=read();m=((n+1)>>1)-1;
	fo(i,1,n) a[i]=read();
	fo(i,n+1,n<<1) a[i]=a[i-n];
	fo(i,1,n) lst[i]=0;
	fo(i,1,n<<1) {
		L[i]=max(L[i-1],lst[a[i]]+1); 
		lst[a[i]]=i;
	}
	fo(i,1,n) lst[i]=n<<1|1;R[n<<1|1]=n<<1|1;
	fd(i,n<<1,1) {
		R[i]=min(R[i+1],lst[a[i]]-1);
		lst[a[i]]=i;
	}
	fo(i,1,n<<1) {
		if (i-L[i]+1>m) L[i]=i-m+1;
		if (R[i]-i+1>m) R[i]=i+m-1;
	}
	fo(i,1,n<<1) s[i]=s[i-1]+R[i];
	ll ans=0;int j=0;
	fo(i,1,n) {
		j=max(j,i);
		while (j<=(n<<1)&&j<=R[i]&&R[j+1]<L[i+n-1]-1) j++;
		if (j<=(n<<1)&&(j<=R[i])) {
			ans+=s[R[i]+1]-s[j];
			ans-=(ll)(L[i+n-1]-2)*(R[i]-j+1);
		}
	}
	printf("%lld\n",ans/3);
	return 0;
}

isaster

构出kruskal重构树,问题变成单点修改子树查询
线段树维护区间乘积即可

Code

#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=2e5+5,Mo=998244353;

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 n,m,q,fa[N][18],f[N],val[N],a[N];

struct Edge{int x,y,z;}e[N];
bool cmp(Edge a,Edge b) {return a.z<b.z;}

int get(int x) {return f[x]?f[x]=get(f[x]):x;}

int jump(int x,int y) {
	fd(i,17,0) if (fa[x][i]&&val[fa[x][i]]<=y) x=fa[x][i];
	return x;
}

int tot,dfn[N],sz[N];

void dfs(int x) {
	dfn[x]=++tot;sz[x]=1;
	rep(i,x) dfs(t[i]),sz[x]+=sz[t[i]];
}

int tr[N<<2];

void modify(int v,int l,int r,int x,int y) {
	if (l==r) {tr[v]=y;return;}
	int mid=l+r>>1;
	if (x<=mid) modify(v<<1,l,mid,x,y);
	else modify(v<<1|1,mid+1,r,x,y);
	tr[v]=(ll)tr[v<<1]*tr[v<<1|1]%Mo;
}

int query(int v,int l,int r,int x,int y) {
	if (x<=l&&r<=y) return tr[v];
	int mid=l+r>>1,tmp=1;
	if (x<=mid) tmp=(ll)tmp*query(v<<1,l,mid,x,y)%Mo;
	if (y>mid) tmp=(ll)tmp*query(v<<1|1,mid+1,r,x,y)%Mo;
	return tmp;
}

int main() {
	n=read();m=read();q=read();
	fo(i,1,n) a[i]=read()%Mo;
	fo(i,1,m) e[i].x=read(),e[i].y=read(),e[i].z=max(e[i].x,e[i].y);
	sort(e+1,e+m+1,cmp);
	int tmp=n;
	fo(i,1,m) {
		int x=get(e[i].x),y=get(e[i].y);
		if (x==y) continue;
		val[++tmp]=e[i].z;
		fa[x][0]=fa[y][0]=tmp;
		add(tmp,x);add(tmp,y);
		f[x]=f[y]=tmp;
	}
	fo(j,1,17) fo(i,1,tmp) fa[i][j]=fa[fa[i][j-1]][j-1];
	dfs(tmp);
	fo(i,1,n) modify(1,1,tmp,dfn[i],a[i]);
	fo(i,n+1,tmp) modify(1,1,tmp,dfn[i],1);
	for(;q;q--) {
		int opt=read(),x=read(),y=read();
		if (opt==1) {
			if (x>y) {puts("0");continue;}
			int z=jump(x,y);
			printf("%d\n",query(1,1,tmp,dfn[z],dfn[z]+sz[z]-1));
		}
		if (opt==2) modify(1,1,tmp,dfn[x],y%Mo);
	}
	return 0;
}

ffort

比赛时想复杂了没写出来(搞了个要写多项式取模的
考虑总和为k,分给左边的方案数为a,分给右边的方案数为b,那么答案就是∑a*b
注意b相当于在k中插n-1个板的方案数
于是我们可以维护多项式Fi(x)F_i(x),x^k的系数表示插k个板的方案数,即j=1bi(jk)=(j+1k+1)[k==0]\sum_{j=1}^{b_i}\binom{j}{k}=\binom{j+1}{k+1}-[k==0]
那么求出Fiai(x)\prod F_i^{a_i}(x)即可
注意我们计算的时候是默认可以在最后一个插板的,实际上是不行的,所以最后需要一个容斥才能求出最终答案
直接快速幂是O(nmlognloga)O(nm \log n \log a),用ln/exp可以做到O(nmlogn)O(nm \log n)

Code

#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--)
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=(1<<18)+5,Mo=998244353;

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

int n,m,a[N],b[N],rev[N],lg,len;
ll W[2][N],inv[N],f[N],g[N],an[N];

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

void DFT(ll *a,int len,int flag) {
	for(int i=0;i<len;i++) if (i<rev[i]) swap(a[i],a[rev[i]]);
	if (flag==-1) flag=0;
	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 u=a[j+k],v=a[j+k+i]*W[flag][i+k]%Mo;
				a[j+k]=(u+v)%Mo;a[j+k+i]=(u-v)%Mo;
			}
	if (flag==0) for(int i=0;i<len;i++) a[i]=a[i]*inv[len]%Mo;
}

void mult(ll *a,ll *b,ll *c) {
	static ll ta[N],tb[N];
	fo(i,0,len-1) ta[i]=tb[i]=0;
	fo(i,0,n-1) ta[i]=a[i],tb[i]=b[i];
	DFT(ta,len,1);DFT(tb,len,1);
	fo(i,0,len-1) ta[i]=ta[i]*tb[i]%Mo;
	DFT(ta,len,-1);
	fo(i,0,n-1) c[i]=ta[i];
}

int main() {
	init(1<<18);
	n=read();m=read();
	for(len=1;len<=(n<<1);len<<=1) lg++;
	fo(i,0,len-1) rev[i]=rev[i>>1]>>1|((i&1)<<lg-1);
	an[0]=1;
	fo(i,1,m) {
		a[i]=read();b[i]=read();
		fo(j,0,n-1) f[j]=(j?f[j-1]*(b[i]+1-j)%Mo*inv[j+1]%Mo:b[i]+1);f[0]--;
		for(;a[i];a[i]>>=1,mult(f,f,f)) if (a[i]&1) mult(an,f,an);
	}
	ll ans=0;
	fo(i,0,n-1) (ans+=((n-1-i)&1?-1:1)*an[i])%=Mo;
	printf("%lld\n",(ans+Mo)%Mo);
	return 0;
}

arewell

先考虑3^n如何Dp
设F[s]表示s内部已经是一个DAG的方案数,考虑枚举入度为0的点,我们有F[s]=tsF[t]2c(st,t)F[s]=\sum_{t\subset s}F[t]*2^{c(s-t,t)}
c(s,t)表示集合s和集合t之间的边数
但是这个是不对的,因为我们只能保证至少那些点是入度为0的而不是恰好所以应该要套一个容斥
F[s]=tsF[t]2c(st,t)(1)popcount(st)+1F[s]=\sum_{t\subset s}F[t]*2^{c(s-t,t)}*(-1)^{popcount(s-t)+1}
这样还不够,我们需要继续优化
设p[s]表示集合s内部的边数,我们有c(s,t)=p(s|t)-p(s)-p(t)
于是就变成了F[s]2p(s)=tsF[t]2p(t)12p(st)(1)popcount(st)+1{F[s]\over 2^{p(s)}}=\sum_{t\subset s}{F[t]\over 2^{p(t)}}*{1\over 2^{p(s-t)}} *(-1)^{popcount(s-t)+1}
然后就是子集卷积的形式了,直接上板子即可
复杂度O(2nn2)O(2^nn^2)
抢了个一血很舒服

Code

#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--)
using namespace std;

typedef long long ll;

const int N=25,S=(1<<20)+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;
}

int f[N][S],g[N][S],cnt[S],p[S],pw[N*N],inv[N*N],n,m,u[N*N],v[N*N];

void FWT(int *a){for(int i=2,j,m,k;m=i>>1,i<=(1<<n);i<<=1)for(int j=0;j<(1<<n);j+=i)for(k=0;k<m;++k)(a[j+k+m]+=a[j+k])%=Mo;}

int main() {
	scanf("%d%d",&n,&m);
	fo(i,1,m) scanf("%d%d",&u[i],&v[i]);
	pw[0]=1;fo(i,1,m) pw[i]=(pw[i-1]<<1)%Mo;
	fo(i,0,m) inv[i]=pwr(pw[i],Mo-2);
	fo(s,1,(1<<n)-1) cnt[s]=cnt[s>>1]+(s&1);
	fo(s,0,(1<<n)-1) fo(i,1,m) if ((s>>(u[i]-1)&1)&&(s>>(v[i]-1)&1)) p[s]++;
	fo(s,1,(1<<n)-1) g[cnt[s]][s]=(cnt[s]&1)?inv[p[s]]:(Mo-inv[p[s]])%Mo;
	fo(i,1,n) FWT(g[i]);
	fo(s,0,(1<<n)-1) f[0][s]=1;
	fo(i,1,n) fo(j,0,i-1) fo(s,0,(1<<n)-1) if (f[j][s]&&g[i-j][s]) f[i][s]=(f[i][s]+(ll)f[j][s]*g[i-j][s])%Mo;
	ll ans=0;
	fo(s,0,(1<<n)-1) ans+=cnt[(1<<n)-1-s]&1?-f[n][s]:f[n][s];
	ans=ans%Mo*pwr(pwr(3,Mo-2),m)%Mo*pw[m]%Mo;
	printf("%d\n",(ans+Mo)%Mo);
	return 0;
}
©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值