WXH♂Round

这几个是YJQ(搬)的题,补补题解好了

T1:WXH要表演,每天可以倒立表演或正常表演,倒立表演有Ai的收益,正常有Bi的收益,要求每连续K天必须有P个倒立,Q个正常

网络流,相当于必须[Q,K-P]个正常表演,设为[l,r],先假设都是倒立表演

建图如下:每一天建一个点,先给前K天r个,表示只能改r个.

i天向i+k天连边,花费Bi-Ai,流量1,表示这一天可以换成正常,但要到i+k天才能再改。

i天向i+1天连边,流量r-l,表示至少要走l个为正常表演

#include<bits/stdc++.h>
#define maxn 300
#define maxm 100000
#define inf 2100000000
using namespace std;
struct edge{int r,nxt,w,cost;}e[maxm];
int S,T,n,a[maxn],b[maxn],vis[maxn],q[600],l,r,K,dis[maxn],P,Q,head[maxn],esz=1,ans;
void addedge(int u,int v,int w,int cost){
	e[++esz].r=v;e[esz].nxt=head[u];head[u]=esz;e[esz].w=w;e[esz].cost=cost;
	e[++esz].r=u;e[esz].nxt=head[v];head[v]=esz;e[esz].w=0;e[esz].cost=-cost;
}
int spfa(){
	for(int i=S;i<=T;++i)dis[i]=inf,vis[i]=0;
	l=r=0,q[r++]=S,dis[S]=0;
	while(l^r){
		int u=q[l++];vis[u]=0;l&=511;
		for(int t=head[u];t;t=e[t].nxt)if(e[t].w&&dis[e[t].r]>dis[u]+e[t].cost)
			dis[e[t].r]=dis[u]+e[t].cost,(vis[e[t].r]?0:q[r++]=e[t].r,r&=511,vis[e[t].r]=1);
	}
	return dis[T]!=inf;
}
int find(int u,int flow){
	if(u==T)return flow;
	vis[u]=1;
	int used=0,a=0;
	for(int t=head[u];t&&used<flow;t=e[t].nxt)if(e[t].w&&dis[e[t].r]==dis[u]+e[t].cost&&!vis[e[t].r])
		a=find(e[t].r,min(flow-used,e[t].w)),e[t].w-=a,e[t^1].w+=a,ans+=e[t].cost*a,used+=a;
	return used;
}
int main(){
	scanf("%d%d%d%d",&n,&K,&r,&l),r=K-r;
	for(int i=1;i<=n;++i)scanf("%d%d",&a[i],&b[i]),ans-=a[i];
	S=0,T=n+2;
	addedge(S,n+1,r,0);
	for(int i=1;i<=K;++i)addedge(n+1,i,inf,0);
	for(int i=1;i<=n;++i){
		addedge(i,i==n?T:i+1,r-l,0);
		addedge(i,i+K>n?T:i+K,1,a[i]-b[i]);
	}
	while(spfa())find(S,inf);
	printf("%d",-ans);
}
T2:3w暴力过了

T3:

容易发现,如果从(1,...1)到(x1,..,xn)的路径数为奇数,则为白点。

因为(1,...1)是白点路径数为奇数,K个前驱中有奇数个白点这个点才是白点,则这个白点路径数也为奇数。

把L[i]--,R[i]--.

考虑2维,则C(x,x+y)%2==1就是白点。

考虑lucas.若x,y二进制下有一位都为1,则产生进位,这样必然存在一个x的二进制位比x+y大,即C(x,x+y)为偶数.

故x,y每个二进制位1的个数<=1

对于n维,路径数为C(x1,x1+...+xn)*C(x2,x2+...xn)*...*C(xn-1,xn-1+xn),可以发现结论依然成立。


考虑数位DP,用3^k来表示每一维是否顶上界,0代表同时顶L,R或同时不顶L,R;1代表顶L;2代表顶R

转移推一推即可

#include<bits/stdc++.h>
#define mod 998244353
#define get(x,y) (!!((x)&(1ll<<(y))))
using namespace std;
long long L[20],R[20];
int n,dp[2][20000],flag[20],pw[20]={1},a[20];
int main(){
	scanf("%d",&n);
	for(int i=1;i<20;++i)
		pw[i]=pw[i-1]*3;
	for(int i=0;i<n;++i)
		scanf("%lld%lld",&L[i],&R[i]),L[i]--,R[i]--;
	int np=0,p=1;
	dp[np][0]=1;
	for(int i=59;i>=0;--i,swap(p,np)){
		int _pos=-1;
		for(int k=0;k<n;++k)if(get(L[k],i)==get(R[k],i)&&get(L[k],i)==1&&!flag[k]){
			if(_pos>=0)return puts("0"),0;
			else _pos=k;
		}
		for(int j=0;j<pw[n];++j)if(dp[np][j]){
		//	printf("<%d,%d,%d>\n",i,j,dp[np][j]);
			int pos=_pos,nj=j;
			for(int k=0;k<n;++k)a[k]=j/pw[k]%3;
			for(int k=0;k<n;++k)
				if(flag[k]&&a[k]==1&&get(L[k],i)==1){
					if(pos>=0)goto nxt; else pos=k;
				}
			for(int k=0;k<n;++k)if(k!=pos){
				if(flag[k]){
					if(a[k]==2&&get(R[k],i)==1)nj-=2*pw[k];
				} else if(get(L[k],i)!=get(R[k],i))
					nj+=pw[k];
			}
			dp[p][nj]=(dp[p][nj]+dp[np][j])%mod;
			if(pos==-1)for(int k=0;k<n;++k){
				int _nj=nj;
				if(flag[k]){
					if(a[k]==1)_nj-=pw[k];
					else if(a[k]==2){
						if(get(R[k],i)==1)_nj+=2*pw[k];
						else continue;
					}
				} else if(get(L[k],i)!=get(R[k],i))
					_nj+=pw[k];
				else continue;
			//	printf("[%d,%d]",k,get(L[k],i));for(int l=0;l<n;++l)printf("%d[%d,%d] ",a[l],get(L[l],i),get(R[l],i));printf("->");
			//	for(int l=0;l<n;++l)printf("%d ",_nj/pw[l]%3);puts("");
				dp[p][_nj]=(dp[p][_nj]+dp[np][j])%mod;
			}
			
			nxt:;dp[np][j]=0;
		}
		for(int k=0;k<n;++k)if(get(L[k],i)!=get(R[k],i))flag[k]=1;
	}
	int ans=0;
	for(int i=0;i<pw[n];++i)ans=(ans+dp[np][i])%mod;
	printf("%d",ans);
}

WXH♂Round #2

T1:有N个WXH,有Vi,Pi,且(Vi,Pi互不相同)YJQ想要硬点一些WXH,给他们标标记.相遇时标记会传递.问有多少种方法使得最后所有的WXH都有标记

可以发现每个人影响的都是一个速度区间(要是Vi可以相同,也是一个区间)

相当于选一些区间覆盖n个点的方案数

令dp[i]表示[1,i]覆盖的方案数

当我们选一个区间[L,R]时,原先的dp[R...n]会*2,因为我们选不选这个区间对覆盖没有影响.再用dp[L-1...R-1]更新dp[R]

模数抄成666623333...智商-1.jpg

#include<bits/stdc++.h>
#define mod 66662333
#define inf 1000000007
#define maxn 400100
using namespace std;
struct data{
	int p,v,s;
	data(){}
	data(int p,int v,int s):
		p(p),v(v),s(s){}
}A[maxn];
struct _query{
	int l,r;
	int operator<(const _query q)const{return l==q.l?r>q.r:l<q.l;}
}q[maxn];
int add[maxn<<2],s[maxn<<2],n,mx[maxn],mn[maxn],lsh[maxn],tp;
int c1(data a,data b){return a.p<b.p;}
void upd(int o){s[o]=(s[o<<1]+s[o<<1|1])%mod;}
void pushdown(int o){
	if(add[o]^1){
		add[o<<1]=1ll*add[o<<1]*add[o]%mod,
		add[o<<1|1]=1ll*add[o<<1|1]*add[o]%mod,
		s[o<<1]=1ll*s[o<<1]*add[o]%mod,
		s[o<<1|1]=1ll*s[o<<1|1]*add[o]%mod,
		add[o]=1;
	}
}
void cheng(int o,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr){add[o]=(add[o]<<1)%mod,s[o]=(s[o]<<1)%mod;return ;}
	int mid=l+r>>1;pushdown(o);
	if(ql<=mid)cheng(o<<1,l,mid,ql,qr);
	if(qr>mid)cheng(o<<1|1,mid+1,r,ql,qr);
	upd(o);
}
void modify(int o,int l,int r,int k,int tg){
	if(l==r){s[o]=(s[o]+tg)%mod;return ;}
	int mid=l+r>>1;pushdown(o);
	if(k<=mid)modify(o<<1,l,mid,k,tg);
	else modify(o<<1|1,mid+1,r,k,tg);
	upd(o);
}
int query(int o,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr)return s[o];
	int mid=l+r>>1,ans=0;pushdown(o);
	if(ql<=mid)ans+=query(o<<1,l,mid,ql,qr);
	if(qr>mid)ans+=query(o<<1|1,mid+1,r,ql,qr);
	return ans%mod;
}
void build(int o,int l,int r){
	add[o]=1;
	if(l==r){
		s[o]=(l==0?1:0);
		return ;
	}
	int mid=l+r>>1;
	build(o<<1,l,mid);
	build(o<<1|1,mid+1,r);
	upd(o);
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
		scanf("%d%d",&A[i].p,&A[i].v),lsh[++tp]=A[i].v;
	sort(A+1,A+n+1,c1),sort(lsh+1,lsh+tp+1);
	for(int i=1;i<=n;++i)
		A[i].v=lower_bound(lsh+1,lsh+tp+1,A[i].v)-lsh;
	mx[0]=0,mn[n+1]=inf;
	for(int i=1;i<=n;++i)
		mx[i]=(mx[i-1]<A[i].v?A[i].v:mx[i-1]);
	for(int i=n;i>=1;--i)
		mn[i]=(mn[i+1]>A[i].v?A[i].v:mn[i+1]);
	for(int i=1;i<=n;++i)
		q[i]=_query{min(A[i].v,mn[i+1]),max(A[i].v,mx[i-1])};
	sort(q+1,q+n+1),build(1,0,n);
	for(int i=1;i<=n;++i){
		cheng(1,0,n,q[i].r,n);
		modify(1,0,n,q[i].r,query(1,0,n,q[i].l-1,q[i].r-1));
	}
	printf("%d",query(1,0,n,n,n));
}

T2:

这题题意好绕。。。

最暴力的暴力大概是这样的:维护一个p=0

每次增加一个字符时,枚举左端点l,然后对于子串[l,r]暴力求fail,然后暴力跳fail且p++,然后ans+=p;

考虑后缀自动机,当增加一个字符时,对于每个right集合包含r的子串,p+=|right集合|

解释:跳fail的过程一定能将所有right集合包含r的子串计算到

代码大概是这样:for(;j;j=fail[j])p+=size[j]*(len[j]-len[fail[j]]),size[j]++;ans+=p;

这样就简单了,只需要换成树链剖分维护就行了

#include<bits/stdc++.h>
#define maxn 200100
#define mod 1000000007
using namespace std;
struct edge{int r,nxt;}e[maxn<<1];
int trie[maxn][26],n,fail[maxn],len[maxn],ptr=1,rt=1,lst=1;
int A[maxn],head[maxn],esz,fa[maxn],top[maxn],son[maxn],id[maxn],fid[maxn],tim;
int s[maxn<<2],addv[maxn<<2],sum[maxn<<2],ans,size[maxn],ans2;
char c[maxn];
void addedge(int u,int v){
	e[++esz].r=v;e[esz].nxt=head[u];head[u]=esz;
}
int dfs1(int u){
	int s,mxs=0,sz=1;
	for(int t=head[u];t;t=e[t].nxt)if(e[t].r!=fa[u]){
		fa[e[t].r]=u,s=dfs1(e[t].r),sz+=s;
		if(mxs<s)mxs=s,son[u]=e[t].r;
	}
	return sz;
}
void dfs2(int u,int tp){
	id[u]=++tim,fid[tim]=u,top[u]=tp;
	if(son[u])dfs2(son[u],tp);
	for(int t=head[u];t;t=e[t].nxt)if(e[t].r!=fa[u]&&e[t].r!=son[u])
		dfs2(e[t].r,e[t].r);
}
void build(int o,int l,int r){
	if(l==r){
		s[o]=len[fid[l]]-len[fail[fid[l]]];
		return ;
	}
	int mid=l+r>>1;
	build(o<<1,l,mid),build(o<<1|1,mid+1,r);
	s[o]=(s[o<<1]+s[o<<1|1])%mod;
}
void modify(int o,int l,int r,int ql,int qr,int tg){
	if(ql<=l&&r<=qr){
		addv[o]+=tg,sum[o]=(sum[o]+1ll*s[o]*tg)%mod;
		return ;
	}
	int mid=l+r>>1;
	if(addv[o])
		addv[o<<1]+=addv[o],addv[o<<1|1]+=addv[o],
		sum[o<<1]=(sum[o<<1]+1ll*addv[o]*s[o<<1])%mod,
		sum[o<<1|1]=(sum[o<<1|1]+1ll*addv[o]*s[o<<1|1])%mod,
		addv[o]=0;
	if(ql<=mid)modify(o<<1,l,mid,ql,qr,tg);
	if(qr>mid)modify(o<<1|1,mid+1,r,ql,qr,tg);
	sum[o]=(sum[o<<1]+sum[o<<1|1])%mod;
}
int query(int o,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr)return sum[o];
	int mid=l+r>>1;
	if(addv[o])
		addv[o<<1]+=addv[o],addv[o<<1|1]+=addv[o],
		sum[o<<1]=(sum[o<<1]+1ll*addv[o]*s[o<<1])%mod,
		sum[o<<1|1]=(sum[o<<1|1]+1ll*addv[o]*s[o<<1|1])%mod,
		addv[o]=0;
	int ans=0;
	if(ql<=mid)ans+=query(o<<1,l,mid,ql,qr);
	if(qr>mid)ans+=query(o<<1|1,mid+1,r,ql,qr);
	return ans%mod;
}
void append(int c){
	int cur=++ptr,p=lst;
	len[cur]=len[lst]+1;
	for(;p&&!trie[p][c];p=fail[p])
		trie[p][c]=cur;
	if(!p){
		fail[cur]=1;
	} else {
		int q=trie[p][c];
		if(len[q]==len[p]+1){
			fail[cur]=q;
		} else {
			int cl=++ptr;
			fail[cl]=fail[q];
			len[cl]=len[p]+1;
			memcpy(trie[cl],trie[q],sizeof(trie[q]));
			for(;p&&trie[p][c]==q;p=fail[p])trie[p][c]=cl;
			fail[cur]=fail[q]=cl;
		}
	}
	lst=cur;
}
void calc(int u){
	while(u){
		ans=(ans+query(1,1,tim,id[top[u]],id[u]))%mod;
		modify(1,1,tim,id[top[u]],id[u],1),u=fa[top[u]];
	}
//	for(;u;u=fail[u])
//		ans=(ans+1ll*size[u]*(len[u]-len[fail[u]]))%mod,size[u]++;
}
int main(){
	scanf("%d%s",&n,c+1);
	for(int i=1;i<=n;++i)
		append(c[i]-'a'),A[i]=lst;
	for(int i=2;i<=ptr;++i)
		addedge(fail[i],i);//,printf("{%d}",fail[i]);
	dfs1(1),dfs2(1,1),build(1,1,tim);
//	for(int i=1;i<=tim;++i)printf("[%d,%d]",top[i],id[i]);
	for(int i=1;i<=n;++i)
		calc(A[i]),printf("%d\n",(ans2+=ans)%=mod);
}
T3:TopCoder 原题(...)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值