8/1 思维+扩展欧几里得+树上dp
Posted 钟钟终
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了8/1 思维+扩展欧几里得+树上dp相关的知识,希望对你有一定的参考价值。
> 活动地址:CSDN21天学习挑战赛
D. Gargari and Permutations
题意:1900难度的dp题,给定k个长度为n的数字排列,找到最长的公共子序列的长度。
思路:
1.属于经典dp问题,最长公共子序列。复杂度在O(n^2)
2.记录每个串数字所在的位置;p[i][a[i][j]]=j
3.对比第二个之后的排列,满足排列1中的数字的相对位置关系,方向应一致不满足则无法进行更新长度
4.f[i]表示截止到i的最长公共徐磊长度。
#include<bits/stdc++.h>
#define int long long
#define endl '\\n'
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int N=5e5+5;
const int mod=1e9+7;
int n,k,a[15][1005],p[15][1005],f[1005];
signed main()
IOS;
cin>>n>>k;
for(int i=1;i<=k;i++)
for(int j=1;j<=n;j++)
cin>>a[i][j],p[i][a[i][j]]=j;
for(int i=1;i<=n;i++)
f[i]=1;
for(int j=1;j<i;j++)
int flag=1;
for(int g=2;g<=k;g++)
if(p[g][a[1][j]]>p[g][a[1][i]])
flag=0;break;
if(flag) f[i]=max(f[i],f[j]+1);
int ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,f[i]);
cout<<ans<<endl;
return 0;
D. Binary String Minimizing
思路:
1.思路倒是不复杂,利用k的次数将0尽可能的向前移动。
2.我得问题是代码敲得太繁琐了。本题可考虑在原串上进行更改,利用swap函数,使用g来记录前面已经放了多少个0,待放‘0’的下标
#include<bits/stdc++.h>
#define int long long
#define endl '\\n'
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int N=5e5+5;
const int mod=1e9+7;
int n,k,pos[N];
string s;
signed main()
IOS;
int q;cin>>q;
while(q--)
cin>>n>>k;
cin>>s;
int g=0;
for(int i=0;i<n;i++)
if(s[i]=='0')
if(i-g<=k)
k-=i-g,swap(s[g],s[i]),g++;
else
swap(s[i],s[i-k]);break;
cout<<s<<endl;
return 0;
CF7C Line
题意:给定一条直线Ax+By+C=0,找到一个点使得横纵坐标都为整数。
思路:
1.刚开始以为是计算几何,没想到本质竟然是扩展欧几里得的裸题。
2.扩欧模板:
解线性方程,定义:
1.定义形如 ax+by=c的方程(其中x,y为未知数)的方程叫做“线性方程”;
2.它的解总是成对出现的(类比线的上点);
3.它等价于求解ax≡c(mod b);
4.它有整数解的充要条件是c%gcd(a,b)=0。
以前写的证明:
#include<bits/stdc++.h>
#define int long long
#define endl '\\n'
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int N=5e5+5;
const int mod=1e9+7;
int a,b,c,ans;
ll exgcd(int A,int B,int &x,int &y) //扩展欧几里得 模板
if(!B)
x=1,y=0;
return A;
int d=exgcd(B,A%B,x,y);
int tmp=x;
x=y , y=tmp-A/B*y;
return d;
signed main()
IOS;
cin>>a>>b>>c;
c=-c;
int x,y;
int d=exgcd(a,b,x,y);
x=c/d*x;
y=c/d*y;
cout<<x<<" "<<y<<endl;
return 0;
P1272 重建道路
思路:
1.f[i][j]
表示保留以i结点为根的子树,结点个数为j,需要删去几条线
2.f[u][1]=c[u]
表示只选u为根需要删去几条边
3.u和e[i].to相连接的节点给砍掉了,但是u和e[i].to相连的的度应该保留,而这段相连的度,在u中算了一次,在edge[i].to中也算了一次,所以应该-2
#include<bits/stdc++.h>
using namespace std;
const int N=5e2+5;
const int inf=0x3f3f3f3f;
int n,p,c[N],f[N][N],head[N],cnt;
struct edge
int nxt,to;
e[N];
void add(int from,int to)
e[++cnt].nxt=head[from];
e[cnt].to=to;
head[from]=cnt;
void dfs(int u,int root)
f[u][1]=c[u];
for(int i=head[u];i;i=e[i].nxt)
if(e[i].to!=root)
dfs(e[i].to,u);
for(int j=p;j>=1;j--)
for(int k=1;k<j;k++)
f[u][j]=min(f[u][j],f[u][k]+f[e[i].to][j-k]-2);
int main()
cin>>n>>p;
for(int i=1;i<n;i++)
int a,b;cin>>a>>b;
c[a]++,c[b]++;
add(a,b);
add(b,a);
memset(f,inf,sizeof(f));
dfs(1,0);
int ans=inf;
for(int i=1;i<=n;i++)
ans=min(f[i][p],ans);
cout<<ans<<endl;
return 0;
以上是关于8/1 思维+扩展欧几里得+树上dp的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces Round #646 (Div. 2) E. Tree Shuffling(树上dp)
Codeforces Round #646 (Div. 2) E. Tree Shuffling(树上dp)