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)

[HAOI2015]树上染色

BZOJ 3572: [Hnoi2014]世界树 [虚树 DP 倍增]

题解[P2664 树上游戏]

树上DP - 最大连通子树 - CodeForces - Maximum White Subtree