LCP 64. 二叉树灯饰,I-Steadily Growing Steam,D - Slime Escape,E - Rectangular Congruen

163 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 28 天,点击查看活动详情

LCP 64. 二叉树灯饰 - 力扣(LeetCode)

分全灭,全灭但根亮,全亮,全亮但根灭四种情况来依次递归分情况讨论就可以,比赛的时候没有想全情况加上代码打的乱就没调出来,,,

class Solution {
public:
    tuple<int,int,int,int> dfs(TreeNode *root){
        if(root==NULL) return{0,0,0,0};
        auto [la,lb,lc,ld]=dfs(root->left);
        auto [ra,rb,rc,rd]=dfs(root->right);
        int v=root->val;
        int x=min({la+ra+(v?1:0),lb+rb+(v?1:2),lc+rc+(v?1:2),ld+rd+(v?3:2)});
        int y=min({la+ra+(v?0:1),lb+rb+(v?2:1),lc+rc+(v?2:1),ld+rd+(v?2:3)});
        int z=min({la+ra+(v?2:1),lb+rb+(v?2:3),lc+rc+(v?0:1),ld+rd+(v?2:1)});
        int k=min({la+ra+(v?1:2),lb+rb+(v?3:2),lc+rc+(v?1:0),ld+rd+(v?1:2)});
        return {x,y,z,k};
    }
    int closeLampInTree(TreeNode* root) {
        auto [x,y,z,k]=dfs(root);
        return min({x,y+1,z+1,k+2});
    }
};

I-Steadily Growing Steam_第 46 届 ICPC 国际大学生程序设计竞赛亚洲区域赛(上海)

竟然没想到这竟然是一个类似背包的问题,设f[i][j][k]为前i个卡片用了j次技能两个集合的差为k最大可以获得多少价值,设出状态来就好转移了,由于差可能为负数所以向右平移2600个距离,ti最大为13,乘以n再乘以2最大是2600;

一共可以有5种状态转移:不选第i个卡片,将第i个卡片放入S集合中,将第i个卡片加倍一下放入S中,将第i个卡片放入T集合中,将第i个卡片加倍一下放入T中,注意一下边界就可以过了

2021 ICPC上海 I.Steadily Growing Steam(dp)_lwz_159的博客-CSDN博客

using namespace std;
const ll mod=1e9+7;
const ll N=1000006,M=2000006;
ll n,m,f[2][110][6005];
struct node{
    ll v,t;
}a[110];
int main(){
   cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i].v>>a[i].t;
    for(int j=0;j<=m;j++)
    for(int k=0;k<=5200;k++) f[0][j][k]=-1e18*(k!=2600);
    int b=0;
    for(int i=1;i<=n;i++){
        b=!b;
        for(int j=0;j<=m;j++)
        for(int k=0;k<=5200;k++){
            f[b][j][k]=f[!b][j][k];
            if(k-a[i].t>=0) f[b][j][k]=max(f[b][j][k],f[!b][j][k-a[i].t]+a[i].v);
            if(k+a[i].t<=5200) f[b][j][k]=max(f[b][j][k],f[!b][j][k+a[i].t]+a[i].v);
            if(j){
                if(k-2*a[i].t>=0) f[b][j][k]=max(f[b][j][k],f[!b][j-1][k-2*a[i].t]+a[i].v);
                if(k+2*a[i].t<=5200) f[b][j][k]=max(f[b][j][k],f[!b][j-1][k+2*a[i].t]+a[i].v);
            }
        }
    }
    cout<<f[b][m][2600]<<endl;
    system("pause");
    return 0;
}

D - Slime Escape 

自己写的是搜索,不出意外的T了;

正确的思路是先判断向左可不可以出去,那么右边的如果可以增加hp就增加,需要预处理出右边的可以加的值和到达这个加的值需要多大的生命值才可以到达;对于向右的思路是一样的重复再判一遍就可以

Codeforces Round #822 (Div. 2) A - E - 知乎 (zhihu.com)

ll t,n,k,a[200005];
bool solve(){
    vector<ll>next,need;
    ll sum=0,minn=1e18;
    for(int i=k+1;i<=n;i++){
        sum+=a[i];
        minn=min(minn,sum);
        if(sum>0){
            next.push_back(sum);
            need.push_back(minn);
            sum=0;minn=0;
        }
    }
    ll cur=a[k],p=0;
    for(int i=k-1;i>=1;i--){
        while(p<next.size()&&cur+need[p]>=0){
            cur+=next[p];
            p++;
        }
        if(cur+a[i]<0) return false;
        cur+=a[i];
    }
    return 1;
}
int main(){
   cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    cin>>t;
    while(t--){
        cin>>n>>k;
        bool flag=false;
        for(int i=1;i<=n;i++) cin>>a[i];
        flag=solve();
        reverse(a+1,a+n+1);
        k=n-k+1;
        flag|=solve();
        if(flag) cout<<"YES\n";
        else cout<<"NO\n";
    }
    system("pause");
    return 0;
}

E - Rectangular Congruence 构造

只知道方法但是不知道如何推出来的,a[i][j]=i*(j-i)+b[i];

这样a[r1][c1]+a[r2][c2]=a[r1][c2]+a[r2][c1]最后化简出来就是(r1-r2)*(c1-c2)=0,可以发现是矛盾的,所以这个构造方法可行

Codeforces Round #822 (Div. 2) A - E - 知乎 (zhihu.com)

ll lcm(ll a,ll b){
    return a*b/__gcd(a,b);
}
ll n,b[405],a[405][405];
int main(){
    //cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>b[i];
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    a[i][j]=(i*(j-i+n)+b[i])%n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
        cout<<a[i][j]<<" ";
        cout<<endl;
    }
    system("pause");
    return 0;
}

B - Meeting on the Line 二分

一看题是让求让所有人中时间最长的那个人时间最小,那么就去想二分了,但是不能二分答案,因为位置这个东西不符合单调性,但是耗的时间是符合的,所以就去二分时间,去看看每个人在这个时间内可以到达的位置是有哪些,然后这n个人位置的交集不是空集说明这个时间是可以的,最后一定会二分到n个人的交集是一个数,那么这就是答案了,但是r-l>1e-8会超时,但是改成1e-7就不会了,其实只跑一百次二分也可以过,,,

ll lcm(ll a,ll b){
    return a*b/__gcd(a,b);
}
int t,n,x[100005],ti[100005];
double xl,xr,ans;
bool check(double mid){
    xl=-1e9,xr=1e9;
    for(int i=1;i<=n;i++){
        double y=mid-ti[i];
        if(y<eps) return 0;
        xl=max(x[i]-y,xl);
        xr=min(x[i]+y,xr);
        if(xl>xr) return 0;
    }
    return 1;
}
signed main(){
    //cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    scanf("%lld",&t);
    while(t--){
        scanf("%lld",&n);
        for(int i=1;i<=n;i++) scanf("%lld",&x[i]);
        for(int i=1;i<=n;i++) scanf("%lld",&ti[i]);
        double l=0,r=2e8;
        while(r-l>1e-7){
            double mid=(l+r)/2;
            if(check(mid)){r=mid;ans=abs(xl);}
            else l=mid;
            //cnt++;
            //if(cnt>100) break;
        }
        printf("%.6lf\n",ans);
    }
    system("pause");
    return 0;
}

C - Minimum Notation

这题比B要简单很多,一共就10个数字一定是数字越小的排在前面越好,从0-9开始遍历先去统计每个0-9每个数的个数,然后再去遍历1-n,让nu表示当前可以保留的数字,如果s[i]!=nu那就+1放入优先队列;否则就去看看队列中是否有小于等于nu的数,有的话就先让小于等于nu的数在前面,之后让nu--,如果tx[nu]<=0那就往后找直到tx[nu]>0

ll lcm(ll a,ll b){
    return a*b/__gcd(a,b);
}
int t,tx[10],ans[200005];
priority_queue<int,vector<int>,greater<int> >q;
signed main(){
    //cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    cin>>t;
    while(t--){
        string s;
        cin>>s;
        for(int i=0;i<10;i++) tx[i]=0;
        int ma=0,cnt=0,nu=0;
        for(int i=0;i<10;i++){
            for(int j=ma;j<s.size();j++){
                int x=s[j]-'0';
                if(x==i){tx[i]++,ma=j;}
            }
        }
        for(int i=0;i<s.size();i++){
            while(tx[nu]<=0) nu++;
            int x=s[i]-'0';
            if(x!=nu) q.push(min(x+1LL,9LL));
            else{
                while(!q.empty()&&x>=q.top()){
                    ans[++cnt]=q.top();q.pop();
                }
                ans[++cnt]=nu;
                tx[nu]--;
            }
        }
        while(!q.empty())
        {
            ans[++cnt]=q.top();q.pop();
        }
        for(int i=1;i<=cnt;i++) cout<<ans[i];
        cout<<endl;
    }
    system("pause");
    return 0;
    //233123112345
}

A - Nucleic Acid Test 二分+并查集+floyd

二分答案,先用floyd处理出任意两点的最短距离,可以发现核算点与核算点之间满足g[i][j]/v<=t就说明这两点是可以互相到达的,用并查集合并这两个点,然后再看核算点与非核算点,假设A和B为核算点,那么为了访问非核算点C,(dA,dB是C到A和B的距离,dB<dA)可以走dA+dB的距离经过点C后在返回核算点,也可以走dB*2的距离,那么一定是走dB的距离更好,所以如果非核算点i与核算点j满足g[i][j]*2/v<=t就合并i和j,根据题意可知道最后如果满足条件的话n个点一定是变成了一个连通块,根据这个返回true或false就可以了

ll lcm(ll a,ll b){
    return a*b/__gcd(a,b);
}
int n,m,k,t,mp[305],vis[405],s[305],ans;
int g[305][305];
int findd(int x){return x==s[x]?x:s[x]=findd(s[x]);}
void floyd()
{
    for(int k=1;k<=n;k++)
    for(int i=1;i<=n;i++)
    if(g[i][k]!=dinf)
    for(int j=1;j<=n;j++)
    g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
}
bool check(int v)
{
    for(int i=1;i<=n;i++) vis[i]=0,s[i]=i;
    for(int i=1;i<=n;i++)
    {
        if(!mp[i]) continue;
        for(int j=1;j<=n;j++)
        {
            if(!mp[j]) continue;
            if((double)g[i][j]/v<=(double)t){
                int x=findd(i),y=findd(j);
                if(x!=y) s[x]=y;
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(mp[i]) continue;
        for(int j=1;j<=n;j++)
        {
            if(!mp[j]) continue;
            if((double)2*g[i][j]/v<=(double)t){
                int x=findd(i),y=findd(j);
                if(x!=y) s[x]=y;
            }
        }
    }
    int res=0;
    for(int i=1;i<=n;i++)
    {
        int x=findd(i);
        if(!vis[x])
        {
            res++;
            vis[x]=1;
            if(res>=2) return 0;
        }
    }
    return 1;
}
signed main(){
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    cin>>n>>m>>k>>t;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    if(i==j) g[i][j]=0;
    else g[i][j]=dinf;
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        cin>>u>>v>>w;
        g[u][v]=g[v][u]=min(g[u][v],w);
    }
    for(int i=1;i<=k;i++)
    {
        int x;
        cin>>x;mp[x]=1;
    }
    floyd();
    if(!t){
        cout<<"-1\n";
        return 0;
    }
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    if(g[i][j]>=dinf)
    {
        cout<<"-1\n";
        return 0;
    }
    int l=1,r=1e18;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(check(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    cout<<ans<<endl;
    system("pause");
    return 0;
    //233123112345
}