ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

暑假集训2

2022-08-12 21:00:57  阅读:158  来源: 互联网

标签:rll ch ll maxn 暑假 集训 dp define


题面

A.LCIS

一道裸的求LCIS(最长公共上升子序列)题.

\(dp\)数组储存到\(b\)的第\(i\)项,\(a\)从\(1-n\)的且以\(b[i]\)结尾的最⻓公共上升⼦序列⻓度.

那么\(dp\)过程显然:

  1. if(a[i]>b[j]&&maxx<f[j]) maxx=f[j];更新可以⽤于更新\(b\)序列与\(a\)序列前\(i\)位的最⻓⻓度的最⼤值.
  2. if(a[i]==b[j]) f[j]=maxx+1;因为\(j\)是内循环,所以如果\(maxx\)被更新了,则现在的\(b[j]\)是等于\(a[i]\),⼀定⽐更新\(maxx\)的\(bj\)⼤.
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define maxn 3001
using namespace std;
static inline ll read()
{
	rll f=0,x=0;rg char ch=getchar();
	while(ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return f?-x:x;
}
static inline void write(rll x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);putchar(x%10|48);
}

ll n,maxx;
ll a[maxn],b[maxn],dp[maxn];
ll ans;
int main()
{
	n=read();
	for(rll i=1;i<=n;i++) a[i]=read();
	for(rll i=1;i<=n;i++) b[i]=read();
	for(rll i=1;i<=n;i++)
	{
		maxx=0;
		for(rll j=1;j<=n;j++)
		{
			if(a[i]==b[j]) dp[j]=maxx+1;
			if(a[i]>b[j]&&maxx<dp[j]) maxx=dp[j];
		}
	}
	for(rll i=1;i<=n;i++) ans=max(ans,dp[i]);
	write(ans);
	return 0;
}

B.平凡的函数

递推题.
在每一次线性筛求素数时筛一遍 \(f(i\times prime[j])\) ,按照题意的三种情况更新即可.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define maxn 50000001
using namespace std;
static inline ll read()
{
	rll f=0,x=0;rg char ch=getchar();
	while(ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return f?-x:x;
}
static inline void write(rll x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);putchar(x%10|48);
}
ll n;
ll dp[maxn];
ll prime[maxn],cnt;
bool fl[maxn];
static inline void xxs()
{
	for(rll i=2;i<maxn;i++)
	{
		if(!fl[i])
		{
			prime[++cnt]=i;
			dp[i]=i^1;
		}
		for(rll j=1;j<=cnt&&i*prime[j]<maxn;j++)
		{
			fl[i*prime[j]]=1;
			rll t=i*prime[j],k=0;
			while(!(t%prime[j])) t/=prime[j],k++;
			if(t==1) dp[i*prime[j]]=prime[j]^k;
			else dp[i*prime[j]]=dp[t]*(prime[j]^k);
			if(!(i%prime[j])) break;
		}
	}
}
int main()
{
	dp[1]=1; xxs();
	n=read();
	for(rll i=1;i<=n;i++) dp[i]+=dp[i-1];
	write(dp[n]);
	return 0;
}

C. 那一天她离我而去

暴力应该很好想,就是把所有和起点相连的边依次删除,每次跑一遍最短路.这样可以得到 \(77pts\) 的好成绩.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define maxn 100001
#define rll rg ll
#define pll pair<ll,ll>
#define inf 4557430888798830399
using namespace std;
inline ll read()
{
	rll f=0,x=0;rg char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return f?-x:x;
}
inline void write(rll x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);putchar(x%10|48);
}
ll t,n,m,ans;
vector<pair<ll,ll> > g[maxn];
ll dis[maxn];
queue<ll> q;
bool fl[maxn];
static inline void spfa(rll x,rll ban)
{
	memset(dis,0x3f,sizeof(dis));
	dis[x]=0;q.push(x);fl[x]=1;
	while(!q.empty())
	{
		rll t=q.front();q.pop();fl[t]=0;
		for(rll i=0;i<g[t].size();i++)
		{
			rll to=g[t][i].first;
			if(t==1&&to!=ban) continue;
			else if(t==ban&&to==n+1) continue;
			if(dis[to]>dis[t]+g[t][i].second)
			{
				dis[to]=dis[t]+g[t][i].second;
				if(!fl[to]) { q.push(to),fl[to]=1; }
			}
		}
	}
	ans=min(ans,dis[n+1]);
}
int main()
{
	t=read();
	while(t--)
	{
		ans=inf;n=read();m=read();
		for(rll i=1;i<=n;i++) g[i].clear();
		for(rll i=1,u,v,w;i<=m;i++) u=read(),v=read(),w=read(),g[u].push_back((pll) { (v==1)?n+1:v,w }),g[v].push_back((pll) { (u==1)?n+1:u,w });
		if(g[1].size()<=1) { puts("-1");continue; }
		for(rll i=0;i<g[1].size();i++) spfa(1,g[1][i].first);
		write(ans==inf?-1:ans);puts("");
	}
	return 0;
}

既然已经想到这了,那么一定可以优化.

考虑分组进行最短路,由于两个不同的数字它们的二进制数一定不同,所以可以将每一个二进制位0和1分组,那么这样每一组点组成的环一定会被更新.

具体流程:

  • 建边时端点为1的特殊处理
  • 枚举二进制的每一位
  • 枚举与点1相连的每一个点
  • 若其二进制下该位为1,将点n+1向该点连边
  • 否则,将该点向点n+2连边
  • 每一次连完所有边后跑一遍从点n+1到点n+2最短路,然后将边删除
  • 统计每次最短路的最小值
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define maxn 80001
#define rll rg ll
#define pll pair<ll,ll>
#define inf 4557430888798830399
using namespace std;
inline ll read()
{
	rll f=0,x=0;rg char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return f?-x:x;
}
inline void write(rll x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);putchar(x%10|48);
}
ll t,len,n,m,ans;
vector<pair<ll,ll> > g[maxn];
ll dis[maxn];
queue<ll> q;
bool fl[maxn];
static inline void spfa(rll x)
{
	memset(dis,0x3f,sizeof(dis));
	dis[x]=0;q.push(x);fl[x]=1;
	while(!q.empty())
	{
		rll t=q.front();q.pop();fl[t]=0;
		for(rll i=0;i<g[t].size();i++)
		{
			rll to=g[t][i].first;
			if(dis[to]>dis[t]+g[t][i].second)
			{
				dis[to]=dis[t]+g[t][i].second;
				if(!fl[to]) { q.push(to),fl[to]=1; }
			}
		}
	}
}
int main()
{
	t=read();
	while(t--)
	{
		for(rll i=1;i<=len;i++) g[i].clear();
		ans=inf;len=n=read();m=read();
		for(rll i=1,u,v,w;i<=m;i++)
		{
			u=read(),v=read(),w=read();
			if(v!=1) g[u].push_back((pll) { v,w });
			if(u!=1) g[v].push_back((pll) { u,w });
		}
		for(rll i=1;i<=n;i<<=1)
		{
			rll x=++len,y=++len;
			for(rll j=0;j<g[1].size();j++)
			{
				if(g[1][j].first&i) g[x].push_back((pll) { g[1][j].first,g[1][j].second });
				else g[g[1][j].first].push_back((pll) { y,g[1][j].second });
			}
			spfa(x);ans=min(ans,dis[y]);
		}
		write(ans==inf?-1:ans);puts("");
	}
	return 0;
}

D.熟练剖分

这题和树链剖分没什么关系,是一道使用树形 \(dp\) 求概率的题.

懒得打式子了,放上std的题解(非本人原创).

以下把题面所求的精彩操作称为最长轻链.

而这个东西显然是可以由子节点转移到父亲节点的.

\(dp[i][j]\)表示在点 i 为根的子树中,向下最长轻链长度为 j 的概率.

对于一个点,先枚举它选择的重儿子是谁,然后扫一遍它的所有儿子,让 \(G[i][j]=Σ_{(k\leq j)}dp[i][k]\),假如当前扫的儿子是 x(x 是重儿子).

\[dp[i][j]=dp[x][j]*G[i][j]+G[x][j]*dp[i][j]-dp[x][j]*dp[i][j] -----(1) \]

不是重儿子的需要相应的改一下,还有要注意 dp 数组更新的顺序,标程是先把 dp 暂存到了一个别的数组里.

转移的时候如果(1)式子的 \(j\) 循环到了 \(size[i]\),那么复杂度可以被卡到 \(N^3\),我们发现当 \(j>size[x]+1\) 的时候 \(dp[x][j]=0\),\(G[x][j]=1\),\(dp[i]\)相当于没有变,所以只要 \(j\) 循环到 \(size[x]+1\) 就行了.

由于每一次循环G数组都需要更新,因此可以压掉一维.

每个节点只有在 dp 它父亲时会被枚举成为重儿子,然后最多把整棵树的大小扫一遍,所以复杂度为 \(O(N^2)\).

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define maxn 3001
#define mod 1000000007
#define rll rg ll
using namespace std;
inline ll read() { rll f=0,x=0; rg char ch=getchar(); while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar(); while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); return f?-x:x; } inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10|48); }
ll n,k[maxn],ans;
vector<ll> g[maxn];
ll rt,rd[maxn],dep[maxn];
ll dp[maxn][maxn];//dp[i][j]为以点i为根的子树中,向下最长轻链长度为j的概率
ll p[maxn];//p[i]:当前重儿子深度为i的概率,即为以上的G
ll h[maxn];//h[i]:当前的最大深度为i的概率
bool fl;
static inline ll ksm(rll a,rll b) { rll ans=1; a%=mod; for(rll i=b;i;i>>=1) { if(i&1) ans=ans*a%mod;a=a*a%mod; } return ans; }
static inline void dfs(rll x)
{
	dep[x]=1;
	if(g[x].empty()) dp[x][0]=1;
	for(rll i=0;i<g[x].size();i++)
	{
		rll to=g[x][i];dfs(to);
		dep[x]+=dep[to];
	}
	for(rll i=0;i<g[x].size();i++)
	{
		for(rll j=0;j<=n;j++) p[j]=1;
		rll t=g[x][i];
		for(rll j=0;j<g[x].size();j++)
		{
			rll to=g[x][j];
			for(rll k=0;k<=dep[to];k++)
			{
				rll tp=p[k],tf;
				if(k) tp-=p[k-1];
				if(t==to)
				{
					tf=dp[to][k];
					if(k) tf-=dp[to][k-1];
					h[k]=(tp*dp[to][k]%mod+tf*p[k]%mod-tp*tf%mod+mod)%mod;
				}
				else
				{
					tf=dp[to][k-1];
					if(k>1) tf-=dp[to][k-2];
					h[k]=(tp*dp[to][k-1]%mod+tf*p[k]%mod-tp*tf%mod+mod)%mod;
				}
			}
			p[0]=h[0];
			for(rll k=1;k<=dep[to];k++) p[k]=(p[k-1]+h[k])%mod;
		}
		for(rll j=dep[x];j;j--) p[j]=(p[j]-p[j-1]+mod)%mod;
		for(rll j=0;j<=dep[x];j++) dp[x][j]=(dp[x][j]+p[j]*ksm(k[x],mod-2)%mod)%mod;
	}
	for(rll i=0;i<=dep[x];i++) dp[x][i]=(dp[x][i]+dp[x][i-1])%mod;
}
int main()
{
	n=read(); for(rll i=1;i<=n;i++) { k[i]=read();for(rll j=1,t;j<=k[i];j++) t=read(),g[i].push_back(t),rd[t]++; }
	for(rll i=1;i<=n;i++) if(!rd[i]) { rt=i;break; }
	dfs(rt);
	for(rll i=n;i;i--) ans=(ans+i*(dp[rt][i]-dp[rt][i-1]+mod)%mod)%mod;
	write(ans%mod);
	return 0;
}


就写这些吧.

标签:rll,ch,ll,maxn,暑假,集训,dp,define
来源: https://www.cnblogs.com/1Liu/p/16581318.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有