1. 程式人生 > >2017HDU多校第9場

2017HDU多校第9場

題目連結

一些題解

02 Ch’s gift

 題目意思是給定一顆有n個節點的樹,和點的點權。有m次查詢,每次查詢範圍在x節點到y節點之間,當沿最短路徑走經過的點權在a - b的範圍內的時候,最終結果就加上這個點權。
 那麼做過LCA的同學可以立馬發現這就是經典的求公共祖先的問題,所以我們每次的路徑其實就是求LCA到兩個節點的路徑,然後預處理所有點到根節點所經歷的點,每次詢問直接三個點的經歷的點判斷一遍就好了。

#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 100000+111;
using
namespace std; int dp[maxn<<1][25]; int ver[maxn<<1]; int dep[maxn<<1]; int quan[100010]; int vis[maxn]; vector<int> dir[maxn]; int first[maxn]; int head[maxn]; int tot,cnt; struct Edge { int to,next; }edge[maxn<<1]; void addedge(int u,int v) { edge[tot].to = v; edge[tot].next = head[u]; head[u] = tot++; } void
init(int n) { memset(dp,0,sizeof(dp)); memset(vis,0,sizeof(vis)); memset(head,-1,sizeof(head)); for(int i=1;i<=n;i++) dir[i].clear(); cnt = tot = 0; } void dfs(int u,int dfn) { vis[u]=1;ver[++cnt] = u;first[u]=cnt;dep[cnt]=dfn; for(int i = head[u];~i;i=edge[i].next) { int
v = edge[i].to; if(!vis[v]) { dir[v].assign(dir[u].begin(),dir[u].end()); dir[v].push_back(quan[v]); dfs(v,dfn+1); ver[++cnt] = u; dep[cnt] = dfn; } } } void ST(int n) { int k = int (log2(n)); for(int i=1;i<=n;i++) dp[i][0]=i; for(int j=1;j<=k;j++) for(int i=1;i+(1<<j)-1<=n;i++) { int a = dp[i][j-1]; int b = dp[i+(1<<(j-1))][j-1]; if(dep[a]<dep[b]) dp[i][j]=a; else dp[i][j]=b; } } int RMQ(int l,int r) { int k = int(log2(r-l+1.0)); int a = dp[l][k]; int b = dp[r-(1<<k)+1][k]; if(dep[a]<dep[b]) return a; return b; } int LCA(int u,int v) { int x = first[u], y = first[v]; if(x>y) swap(x,y); return ver[RMQ(x,y)]; } int main() { int n,m; while(~scanf("%d %d",&n,&m)) { init(n); for(int i=1;i<=n;i++) scanf("%d",&quan[i]); for(int i=1;i<=n-1;i++) { int a,b; scanf("%d %d",&a,&b); addedge(a,b); addedge(b,a); } dir[1].push_back(quan[1]); dfs(1,1);ST(2*n-1); while(m--) { int s,t,a,b; scanf("%d%d%d%d",&s,&t,&a,&b); int v=LCA(s,t); ll sum=0; for(int i=0;i<dir[s].size();i++) { if(dir[s][i]<=b&&dir[s][i]>=a) sum+=dir[s][i]; } for(int i=0;i<dir[t].size();i++) { if(dir[t][i]<=b&&dir[t][i]>=a) sum+=dir[t][i]; } for(int i=0;i<dir[v].size();i++) { if(dir[v][i]<=b&&dir[v][i]>=a) sum-=2*dir[v][i]; } if(quan[v]>=a&&quan[v]<=b) sum+=quan[v]; printf("%I64d",sum); if(m==0) printf("\n"); else printf(" "); } } return 0; }

05 FFF at Valentine

 這題的意思就是讓所有兩個點之間都有一條連通的道路。
 那麼就從前往後搜,當搜一個點時發現它不能到其中一個點,那麼就反過來搜看看那個點能不能到它,dfs即可。

#include <bits/stdc++.h>
using namespace std;
bool vis[1010];
vector<int >a[1010];
void dfs(int s)
{
    vis[s]=1;
    for(int i=0;i<a[s].size();i++)
    {
        if(vis[a[s][i]]==0) dfs(a[s][i]);
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d %d",&n,&m);
        int x,y;
        for(int i=1;i<=n;i++)
            a[i].clear();
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            a[x].push_back(y);
        }
        bool flag=1;
        for(int i=1;i<n;i++)
        {
            memset(vis,0,sizeof(vis));
            dfs(i);
            vector<int>v;
            for(int j=i+1;j<=n;j++)
            {
                if(vis[j]==0)
                {
                    v.push_back(j);
                }
            }
            for(int j=0;j<v.size();j++)
               {
                    memset(vis,0,sizeof(vis));
                    dfs(v[j]);
                    if(vis[i]==0)
                    {
                        flag=0;
                        break;
                    }
               }
            if(flag==0) break;
        }
        if(flag==1) printf("I love you my love and our love save us!\n");
             else printf("Light my fire!\n");



    }

}

08 Numbers

 這題的意思就是一個有n個元素的a序列,每兩個元素相加組成有n*(n - 1) / 2個元素的b序列,現在將a序列和b序列混雜,問原來的a序列是什麼。
 可以知道,大的數都是由小的數推出來的,所以從小到大排序並記錄當前數出現的個數。從小到大跑一遍,當前數的個數不為0時就把此數和之前放進答案的數相加的數的次數減一,最後把此數放入答案,而最後當放滿sqrt(2 * m + 0.25)個就是答案。

#include <bits/stdc++.h>
using namespace std;
int stand[125255];
int ans[1000];
map<int,int>M;
int main()
{
    int m;
    while(scanf("%d",&m) != EOF)
    {

        M.clear();
        int n=sqrt(2*m+0.25);
        memset(ans, 0, sizeof(ans));
        for(int i=1;i<=m;i++)
            scanf("%d", &stand[i]), M[stand[i]]++;
        if(m==0) cout<<"0"<<endl;
        else if(m==1)
            {
                cout<<"1"<<endl;
                cout<<stand[1]<<endl;
            }
      else
        {
            sort(stand + 1, stand + 1 + m);
            int start=3;
            int cnt=2;
            ans[1]=stand[1],ans[2]=stand[2];
            M[ans[1]+ans[2]]--;
            while(1)
            {
                if(cnt==n) break;
                for(int i=start;i<=m;i++)
                {
                    if(M[stand[i]]!=0)
                    {
                        M[stand[i]]--;
                        start=i + 1;
                        break;
                    }
                }
                ans[++cnt]=stand[start - 1];
                for(int i=1;i<cnt;i++)
                {
                    M[ans[i]+ans[cnt]]--;
                }

            }
            printf("%d\n",n);
            for(int i=1;i<=cnt;i++)
            {
                printf("%d",ans[i]);
                if(i==cnt) printf("\n");
                else printf(" ");
            }
        }
    }
}

補題

10 Two strings

 題目給定兩個字串,第一個字串只有字母,第二個裡面包含”.“和”“,”.“可以代表任意字元,”“可以代表把前面那個字母刪掉,或者是空或者任意個前面那個字元的重複。
 比賽的時候想著模擬,賽後看了網上題解程式碼才發現是dp…….直接dp[i][j]代表去匹配的字串到第i個的時候,被匹配字串到第j個的時候能否匹配的上即可。
 ps:網上還有用regex的大神,學習了。

#include <bits/stdc++.h>
using namespace std;
char s1[5000], s2[5000];
int dp[2510][2510];
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        memset(dp, 0, sizeof(dp));
        scanf("%s%s", s1 + 1, s2 + 1);
        int len1 = strlen(s1 + 1), len2= strlen(s2 + 1);
        dp[0][0] = 1;
        for(int i = 1;i <= len2;i++)
        {
            if(s2[i] == '*'&&i == 2)
                dp[i][0] = 1;
            for(int j = 1;j <= len1;j++)
            {
                if(s2[i] == '.')
                    dp[i][j] = dp[i - 1][j - 1];
                else if(s2[i] != '*')
                {
                    if(s2[i] == s1[j])
                        dp[i][j] = dp[i - 1][j - 1];
                }
                else
                {
                    dp[i][j] = max(dp[i - 1][j], dp[i - 2][j]);
                    if(dp[i][j - 1]&&s1[j - 1] == s1[j])
                        dp[i][j] = 1;
                }
            }
        }
        if(dp[len2][len1])
            printf("yes\n");
        else
            printf("no\n");
    }
    return 0;
}