NOIP提高组模拟赛19

a

\(N\)很小\(M\)很大,猜测需要\(O(N^2M)\)的算法,就是枚举\(N\)得到一段序列然后\(O(M)\)求贡献

随着左界的右移,满足条件的区间一定不会左移,使用双指针(或许是三个

code
#include<cstdio>
#include<cstring>

using namespace std;

typedef long long ll;
const int maxn=50005;
int mp[35][maxn],n,m;
char c[maxn];


int sum(int i,int j,int l,int r){
    return mp[j][r]-mp[i-1][r]-mp[j][l-1]+mp[i-1][l-1];
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        scanf("%s",c+1);
        for(int j=1;j<=m;++j)
         mp[i][j]=c[j]-'0'+mp[i-1][j]+mp[i][j-1]-mp[i-1][j-1];
    }
    int l,r;scanf("%d%d",&l,&r);
    ll ans=0;
    for(int i=1;i<=n;++i)
     for(int j=i;j<=n;++j){
        int p1=1,p2=1;
        for(int k=1;k<=m;++k){
            while(p1<=m&&sum(i,j,k,p1)<l)++p1;
            while(p2<=m&&sum(i,j,k,p2)<=r)++p2;
            if(p1<k)p1=k;
            ans=ans+p2-p1;
        }
     }
    printf("%lld\n",ans);
    return 0;
}

b

发现值域不大,可以计算\(LCA=i\)的方案数,最后统计答案,复杂度\(O(NMa_{max})\)

考虑优化

虽然不能很快求出\(LCA=i\)的方案数,但是可以较快求出\(i|GCD\)的方案数

然后这个东西可以简单容斥掉,使用莫比乌斯反演可以更快(我不会

如何求出\(i|GCD\)的方案数,选的每个数都是\(i\)的倍数,那么

\(𝑂(𝑛(𝑚+𝑥ln𝑥)\))处理出第\(i\)行有多少个数是\(j\)的倍数,记为\(𝑐𝑛𝑡_{i,j}\)

共有\(∏ _{i=1}^n(𝑐𝑛𝑡_{𝑖,𝑗} +1)-1\)种方案,使得选的所有数均为\(j\)的倍数,复杂度\(O(NM)\)

code
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

typedef long long ll;
const int mod=1e9+7;
const int maxn=1e5+5;

int mp[23][maxn],n,m,x;
int cnt[23][maxn];
int ans[maxn];

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
      for(int j=1;j<=m;++j)
	  {
		  int y;scanf("%d",&y);
		  ++mp[i][y];
		  x=max(x,y);
	  }
	for(int i=1;i<=n;++i){
		for(int k=1;k<=x;++k)
		  for(int p=k;p<=x;p+=k)
		   cnt[i][k]+=mp[i][p];
	}

	for(int i=1;i<=x;++i){
		ans[i]=1;
		for(int j=1;j<=n;++j)
			ans[i]=1ll*ans[i]*(cnt[j][i]+1)%mod;
		ans[i]=(1ll*ans[i]-1+mod)%mod;
	}
	int pr=0;
	for(int i=x;i;--i){
		for(int j=i+i;j<=x;j+=i)
		  ans[i]=(1ll*ans[i]-ans[j]+mod)%mod;
		pr=(1ll*ans[i]*i%mod+pr)%mod;
	}
	printf("%d\n",pr);
    return 0;
}

c

如果忽略重边,那么原图就是一棵树

发现我们要尽量选择颜色不同的边来走,那么对于颜色很多的边,对颜色去重,保留任意三种不同颜色一定可以得到答案

然后需要一个\(DP\)解决问题,由于蒟蒻不会点分治,使用倍增实现

\(f[i][j][l][r]\)表示从\(i\)号点到向上\(2^j\)级祖先,第一条边颜色为\(i\),最后一条边颜色为\(j\)的答案,然后倍增即可

使用结构体可以更方便的进行操作

code
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;

const int maxn=1e5;
int n,m,head[maxn],tot,fa[maxn][19],dep[maxn];
struct DP{
	int c1[3],c2[3],p1,p2,a[3][3];
	DP(){memset(a,0,sizeof(a));p1=-1;p2=-1;}
	
	void pre(int col){
		c1[++p1]=col;
		c2[++p2]=col;
		a[p1][p2]=1;
	}
	
	friend DP operator + (const DP &x,const DP &y){
		DP ans;ans.p1=x.p1;ans.p2=y.p2;
		for(int i=0;i<=ans.p1;++i)ans.c1[i]=x.c1[i];
		for(int i=0;i<=ans.p2;++i)ans.c2[i]=y.c2[i];
		for(int l=0;l<=ans.p1;++l){
			for(int r=0;r<=ans.p2;++r){
				for(int i=0;i<=x.p2;++i){
					for(int j=0;j<=y.p1;++j){
						int ls=x.a[l][i]+y.a[j][r];
						if(x.c2[i]==y.c1[j])--ls;
						ans.a[l][r]=max(ans.a[l][r],ls);
					}
				}
			}
		}
		return ans;
	}
	void res(){
		DP ls;ls.p1=p2;ls.p2=p1;
		for(int i=0;i<=ls.p1;++i)
		 for(int j=0;j<=ls.p2;++j)
		   ls.a[i][j]=a[j][i];
		for(int i=0;i<=ls.p1;++i)ls.c1[i]=c2[i];
		for(int i=0;i<=ls.p2;++i)ls.c2[i]=c1[i];
		(*this)=ls;
	}

	void get_ans(){
		int ans=0;
		for(int i=0;i<=p1;++i)
		 for(int j=0;j<=p2;++j)
		   ans=max(ans,a[i][j]);
		printf("%d\n",ans);
	}
}dp[maxn][19];

struct edge{int net,to,col;}e[maxn*6+5];
struct ll{int u,v,w;}ee[maxn*6+5];
bool cmp(ll x,ll y){return x.u==y.u?x.v<y.v:x.u<y.u;}
void add(int u,int v,int w){
    e[++tot].net=head[u];
    head[u]=tot;
    e[tot].to=v;
    e[tot].col=w;
}
void DFS(int x){
	for(int i=head[x];i;i=e[i].net){
		int v=e[i].to;
		if(v==fa[x][0])continue;
		dp[v][0].pre(e[i].col);
		if(fa[v][0])continue;
		dep[v]=dep[x]+1;
		fa[v][0]=x;
		DFS(v);
	}
}

void work(int u,int v){
	if(u==v){
		printf("0\n");
		return;
	}

	if(dep[v]>dep[u])swap(u,v);
	DP lu,lv;bool f1=1,f2=1;
	for(int i=18;i>=0;--i)
		if(dep[u]-dep[v]>=(1<<i)){
			if(f1)lu=dp[u][i],f1=0;
			else lu=lu+dp[u][i];
			u=fa[u][i];
		}
	if(u==v){
		lu.get_ans();
		return;
	}
	for(int i=18;i>=0;--i)
		if(fa[u][i]!=fa[v][i]){
			if(f1)lu=dp[u][i],f1=0;
			else lu=lu+dp[u][i];
			if(f2)lv=dp[v][i],f2=0;
			else lv=lv+dp[v][i];
			u=fa[u][i];
			v=fa[v][i];
		}
	if(f1)lu=dp[u][0];
	else lu=lu+dp[u][0];
	lu=lu+dp[v][0];
	if(!f2){
		lv.res();
		lu=lu+lv;
	}
	lu.get_ans();
	return;
}


int main()
{
    scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i){
		scanf("%d%d%d",&ee[i].u,&ee[i].v,&ee[i].w);
		if(ee[i].u>ee[i].v)swap(ee[i].u,ee[i].v);
	}
	sort(ee+1,ee+m+1,cmp);
	int lu=-1,lv,num,c1,c2;
	for(int i=1;i<=m;++i){
		if(ee[i].u==lu&&ee[i].v==lv){
			if(num>=3)continue;
			if(c1==ee[i].w)continue;
			if(num==2&&c2==ee[i].w)continue;
			++num;c2=ee[i].w;	
		}else{
			num=1;lu=ee[i].u;lv=ee[i].v;c1=ee[i].w;
		}
		add(ee[i].u,ee[i].v,ee[i].w);
		add(ee[i].v,ee[i].u,ee[i].w);
	}
	DFS(1);
	for(int j=1;j<=18;++j)
	  	for(int i=1;i<=n;++i){
	    	fa[i][j]=fa[fa[i][j-1]][j-1];
			dp[i][j]=dp[i][j-1]+dp[fa[i][j-1]][j-1];
		}
	
	int Q;scanf("%d",&Q);
    for(int i=1;i<=Q;++i)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        work(x,y);
    }


    return 0;
}
posted @ 2022-03-15 17:27  Chen_jr  阅读(38)  评论(0编辑  收藏  举报