Niu Ke OI Weekly 15-Improve group (A tree shape dp seek subtree diameter B greedy, C (bitset optimization 01 backpack))

Title link

A-Global Travel

Topic:

Practice: Reference from: this

Vector time out, only use chain forward star to A

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
const int N=1e6+10;
struct node
{
    int u,w,ne;
}e[N*2];
int head[N],cnt;
void add(int u,int v,int w)
{
    e[++cnt]={v,w,head[u]};
    head[u]=cnt;
    e[++cnt]={u,w,head[v]};
    head[v]=cnt;
}

vector<pii >edge;
int n;
int mx[N][2],f[N][2];
int sum=0,flag,l,r;
void dfs1(int u,int fa,int dep)
{
    if(dep>sum){
        sum=dep;
        if(!flag) l=u;
        else r=u;
    }
    for(int i=head[u];i;i=e[i].ne){
        int v=e[i].u;
        int w=e[i].w;
        if(v==fa) continue;
        dfs1(v,u,dep+w);
    }
}
void dfs2(int u,int fa)
{
    if(u==r){
        flag=1;return;
    }
    for(int i=head[u];i;i=e[i].ne){
        int v=e[i].u;
        int w=e[i].w;
        if(v==fa) continue;
        edge.push_back(make_pair(u,v));
        dfs2(v,u);
        if(flag) return ;
        edge.pop_back();
    }
}
void init()
{
    dfs1(1,-1,0);
    sum=0,flag=1;
    dfs1(l,-1,0);//获取直径的两个点
    flag=0;
    dfs2(l,-1);//获取直径的信息
}
void dfs3(int u,int fa,int flag)
{
    int mx1=0,mx2=0;
    for(int i=head[u];i;i=e[i].ne){
        int v=e[i].u;
        int w=e[i].w;
        if(v==fa) continue;
        dfs3(v,u,flag);
        int t=mx[v][flag]+w;

        if(t>=mx1) mx2=mx1,mx1=t;
        else if(t>mx2) mx2=t;

        mx[u][flag]=max(mx[u][flag],mx[v][flag]+w);
        f[u][flag]=max(f[u][flag],f[v][flag]);
    }
    //if(!flag) printf("u:%d mx1:%d mx2:%d\n",u,mx1,mx2);
    f[u][flag]=max(f[u][flag],mx1+mx2);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;++i){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);

    }
    init();
    //printf("l:%d r:%d\n",l,r);

    dfs3(l,-1,0);
    dfs3(r,-1,1);


//    for(int i=1;i<=n;++i){
//        printf("i:%d r:%d mx:%d\n",i,f[i][0],mx[i][0]);
//    }


    int ans=1e9;
    for(auto now:edge)
    {
        ans=min(ans,max(f[now.first][1],f[now.second][0]));
    }
    cout<<ans<<endl;
}

B-Recovery series

Topic:

Starting from the highest one to the rear, each time you remove it, you will lose one and add six, so the real increase is five, and the problem is guaranteed to be solved.

Then it can be removed from the highest bit n, just use the queue. Since the number sequence ai is also output, it is necessary to record the historical state. Pop cannot be saved with a vector. Use a left pointer to find the number that can be disassembled.

#include<bits/stdc++.h>
using namespace std;
vector<pair<int,int>>ans;
int n,x;
int main()
{
    scanf("%d%d",&n,&x);
    int len=x;
    ans.push_back(make_pair(x,n-1));
    int i=0;
    while(len<n-1){
        while(ans[i].first==0) ++i;
        len+=x-1;
        ans[i].first--;
        ans.push_back(make_pair(x,ans[i].second-1));
    }
 
    printf("%d ",n);
    for(auto now:ans){
        while(now.first--) printf("%d ",now.second);
    }
}

 

C-go back

Topic:

Find out which capsules are necessary.

Sample analysis:

You can simply think of 01 backpack thought:

So the timeout code:

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair<int, int>
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int N=1e5+10;
//bitset<N>pre[500],last[500];
int a[N],n,m;
bool pre[500][N],last[500][N];
map<int,int>mp;
int main()
{
    cin>>n>>m;
    rep(i,1,n){scanf("%d",&a[i]);mp[a[i]]++;}
    sort(a+1,a+1+n);n=unique(a+1,a+1+n)-a-1;
 
 
     
    pre[0][0]=1;
    for(int i=1;i<=n;++i){
        for(int j=0;j<=m;++j){
            pre[i][j]=pre[i-1][j];
            int t=mp[a[i]];
            for(int k=1;k<=t;++k)
            if(j>=k*a[i]) pre[i][j]|=pre[i-1][j-k*a[i]];
        }
    }
 
    last[n+1][0]=1;
    for(int i=n;i>=1;--i){
        for(int j=0;j<=m;++j){
            last[i][j]=last[i+1][j];
            int t=mp[a[i]];
            for(int k=1;k<=t;++k)
            if(j>=k*a[i])last[i][j]|=last[i+1][j-k*a[i]];
        }
    }
 
 
    vector<int>ans;
    for(int i=1;i<=n;++i){
        int flag=1;
        for(int j=0;j<=m&&flag;++j){
            if(pre[i-1][j]&&last[i+1][m-j]){
                flag=0;
            }
        }
        if(flag){
            ans.push_back(a[i]);
        }
    }
 
    sort(ans.begin(),ans.end());
    printf("%d\n",ans.size());
    for(int v:ans)printf("%d ",v);
}

Then look at the bitset optimization 01 backpack

Corresponding question type: For this kind of different types, there are a number of> = 1. Because the sum of the number between the types is 1e5 state is also 1e5. The ordinary 01 backpack must timeout.

Bitset optimization, adopting the characteristics of binary, the number of the same type can be iterated by multiplying the coverage state, a bit like a fast power.

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
#include<cmath>
#include<bitset>
using namespace std;
typedef long long ll;
const int N = 100010;
bitset<N>f[451],g[451];

 
int a[N],cnt[N],ans[N],anss;
int main() {
   
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;++i) scanf("%d",&a[i]);
 
    sort(a + 1,a + n + 1);
 
 
    int tot = 0;
    for(int i = 1;i <= n;++i) {
        if(a[i] != a[i - 1])  a[++tot] = a[i];
        cnt[tot]++;
    }
 
    n = tot;
 
    f[0][0] = 1;
    g[n + 1][0] = 1;
     
    for(int i = 1;i <= n;++i) {
        int k = a[i],tmp = cnt[i];//f[i]|=f[i]<<a[i]代表f[i]取一个a[i]时的状态转移 
         
        f[i] = f[i - 1];
         
        for(int j = 1;j <= tmp;tmp -= j,j <<= 1,k <<= 1) f[i] |= f[i] << k;
        // 运用倍增(二进制)的思想,节约时间 并且能够覆盖所有的状态
        //比如现在有5个1  
        //j=1 1一个1  可以 得到 bitset状态:000011(从后往前数从低位到高位,低位从0开始)
        //j=2 那么倍增一下,两个1 :之前的状态00011移两位得  001100 
        //或上之前得000011  得    001111   是不是得到0,1,2,3,都是1的情况
        //需要注意的是现在我们应该是消耗了三个1了 j目前还是2。那么tmp就不是一成不变的,所以tmp-=j
        //接着j继续乘2  j =4  由于5个1消耗了3  剩余两个,小于j   跳出for循环
         //因为有剩余的部分,就继续组合一下
        f[i] |= f[i] << (a[i] * tmp);
    }
 
    for(int i = n;i >= 1;--i) {
        int k = a[i],tmp = cnt[i];
         
        g[i] = g[i + 1];
         
        for(int j = 1;j <= tmp;tmp -= j,j <<= 1,k <<= 1) g[i] |= g[i] << k;
     
        g[i] |= g[i] << (a[i] * tmp);
    }
 
    for(int i = 1;i <= n;++i) {
        int flag = 0;
        for(int j = 0;j <= m;++j) {
            if(f[i - 1][j] & g[i + 1][m - j]) {
                flag = 1;break;
            }
        }
        if(!flag) ans[++anss] = a[i];
    }
 
//  for(int i = 1;i <= n;++i) printf("%d %d\n",a[i],cnt[i]);
 
    printf("%d\n",anss);
    for(int i = 1;i <= anss;++i) printf("%d ",ans[i]);
    return 0;
}

 

 

Published 519 original articles · praised 69 · 50,000+ views

Guess you like

Origin blog.csdn.net/qq_41286356/article/details/105324165