暑假集训2
题面
A.LCIS
一道裸的求LCIS(最长公共上升子序列)题.
数组储存到的第项,从的且以结尾的最⻓公共上升⼦序列⻓度.
那么过程显然:
if(a[i]>b[j]&&maxx<f[j]) maxx=f[j];
更新可以⽤于更新序列与序列前位的最⻓⻓度的最⼤值.if(a[i]==b[j]) f[j]=maxx+1;
因为是内循环,所以如果被更新了,则现在的是等于,⼀定⽐更新的⼤.
点击查看代码
#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.平凡的函数
递推题.
在每一次线性筛求素数时筛一遍 ,按照题意的三种情况更新即可.
点击查看代码
#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. 那一天她离我而去
暴力应该很好想,就是把所有和起点相连的边依次删除,每次跑一遍最短路.这样可以得到 的好成绩.
点击查看代码
#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.熟练剖分
这题和树链剖分没什么关系,是一道使用树形 求概率的题.
懒得打式子了,放上std的题解(非本人原创).
以下把题面所求的精彩操作称为最长轻链.
而这个东西显然是可以由子节点转移到父亲节点的.
表示在点 i 为根的子树中,向下最长轻链长度为 j 的概率.
对于一个点,先枚举它选择的重儿子是谁,然后扫一遍它的所有儿子,让 ,假如当前扫的儿子是 x(x 是重儿子).
不是重儿子的需要相应的改一下,还有要注意 dp 数组更新的顺序,标程是先把 dp 暂存到了一个别的数组里.
转移的时候如果(1)式子的 循环到了 ,那么复杂度可以被卡到 ,我们发现当 的时候 ,,相当于没有变,所以只要 循环到 就行了.
由于每一次循环G数组都需要更新,因此可以压掉一维.
每个节点只有在 dp 它父亲时会被枚举成为重儿子,然后最多把整棵树的大小扫一遍,所以复杂度为 .
点击查看代码
#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;
}
就写这些吧.
--END--
我的博客: 𝟷𝙻𝚒𝚞
本文链接: https://www.cnblogs.com/1Liu/p/16581318.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!