2012 ACM/ICPC Asia Regional Changchun Online(hdu 4267 - 4277)线段树+dp+dfs+树形dp

A Simple Problem with Integers

Problem Description
Let A1, A2, … , AN be N elements. You need to deal with two kinds of operations. One type of operation is to add a given number to a few numbers in a given interval. The other is to query the value of some element.

Input
There are a lot of test cases.
The first line contains an integer N. (1 <= N <= 50000)
The second line contains N numbers which are the initial values of A1, A2, … , AN. (-10,000,000 <= the initial value of Ai <= 10,000,000)
The third line contains an integer Q. (1 <= Q <= 50000)
Each of the following Q lines represents an operation.
“1 a b k c” means adding c to each of Ai which satisfies a <= i <= b and (i - a) % k == 0. (1 <= a <= b <= N, 1 <= k <= 10, -1,000 <= c <= 1,000)
“2 a” means querying the value of Aa. (1 <= a <= N)

Output
For each test case, output several lines to answer all query operations.

Sample Input

4
1 1 1 1
14
2 1
2 2
2 3
2 4
1 2 3 1 2
2 1
2 2
2 3
2 4
1 1 4 2 1
2 1
2 2
2 3
2 4

Sample Output

1
1
1
1
1
3
3
1
2
3
4
1
思路:根据每个数相对起点的对k取余得到余数的值维护多棵线段树

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=50010;

int N,M;
int a[maxn];
struct BIT{
    int a[maxn];
    void clear(){
        memset(a,0,sizeof(a));
    }
    void update(int l,int r,int c){
        while(l<=N+1){
            a[l]+=c;
            l+=lowbit(l);
        }
        r++;
        while(r<=N+1){
            a[r]+=-c;
            r+=lowbit(r);
        }
    }
    int lowbit(int x){
        return x&(-x);
    }
    int getsum(int x){
        int sum=0;
        while(x){
            sum+=a[x];
            x-=lowbit(x);
        }
        return sum;
    }
}tree[11][11];
int main(){
    while(scanf("%d",&N)!=EOF){
        for(int i=1;i<=N;i++){
            scanf("%d",&a[i]);
        }
        for(int i=0;i<=10;i++){
            for(int j=0;j<=10;j++){
                tree[i][j].clear();
            }
        }
        int op,x,y,k,c;
        scanf("%d",&M);
        while(M--){
            scanf("%d",&op);
            if(op==1){
                scanf("%d%d%d%d",&x,&y,&k,&c);
                int j=(x-1)%k;
                tree[k][j].update(x,y,c);
            } else {
                scanf("%d",&x);
                int ans=0;
                for(int i=1;i<=10;i++){
                    ans+=tree[i][(x-1)%i].getsum(x);
                }
                printf("%d\n",ans+a[x]);
            }
        }
    }
    return 0;
}

Alice and Bob

Problem Description
Alice and Bob’s game never ends. Today, they introduce a new game. In this game, both of them have N different rectangular cards respectively. Alice wants to use his cards to cover Bob’s. The card A can cover the card B if the height of A is not smaller than B and the width of A is not smaller than B. As the best programmer, you are asked to compute the maximal number of Bob’s cards that Alice can cover.
Please pay attention that each card can be used only once and the cards cannot be rotated.

Input
The first line of the input is a number T (T <= 40) which means the number of test cases.
For each case, the first line is a number N which means the number of cards that Alice and Bob have respectively. Each of the following N (N <= 100,000) lines contains two integers h (h <= 1,000,000,000) and w (w <= 1,000,000,000) which means the height and width of Alice’s card, then the following N lines means that of Bob’s.

Output
For each test case, output an answer using one line which contains just one number.

Sample Input

2
2
1 2
3 4
2 3
4 5
3
2 3
5 7
6 8
4 1
2 5
3 4

Sample Output

1
2

思路:详见代码把,写的很清楚,其实就是每次找离他最近的点

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
multiset<int> sp;
multiset<int>::iterator it;
const int maxn=100100;
struct node{
    int x,y;
    bool operator<(const node& a)const{
        return xint N;
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        sp.clear();
        scanf("%d",&N);
        for(int i=1;i<=N;i++){
            scanf("%d%d",&a[i].x,&a[i].y);
        }
        for(int i=1;i<=N;i++){
            scanf("%d%d",&b[i].x,&b[i].y);
        }
        sort(a+1,a+1+N);
        sort(b+1,b+1+N);
        int j=1,ans=0;
        for(int i=1;i<=N;i++){
            while(j<=N&&b[j].x<=a[i].x){
                sp.insert(b[j].y);
                j++;
            }
            it=sp.upper_bound(a[i].y);
            if(sp.size()>0&&it!=sp.begin())it--;
            if(sp.size()>0&&(*it)<=a[i].y){
                ans++;
                sp.erase(it);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

Find Black Hand

Problem Description
I like playing game with my friends, although sometimes look pretty naive. Today I invent a new game called find black hand. The game is not about catching bad people but playing on a string.
Now I generate a string S and several short ones s[i], and I define three kinds of operations.
1. Delete: remove the ith character.
2. Insert: in any position, insert a character if you like.
3. Change: change the ith character into another character if you like.
For each short string s[i], we define a function f(i). After several operations on S, we can find a substring of S which is the same to s[i]. And f(i) is the minimal number of operations to achieve. It looks so native that I think every one of you can solve f(i) perfectly. So I join the string S from end to end, and f(i) changes nothing. So the string “bb” is also a substring of string “baaab”.
The “black hand” is the short string s[i] whose f(i) is minimal. Now it’s your time to find the black hand.

Input
There are multiple test cases.
The first line contains a non-empty string S whose length is not more than 100,000.
The next line contains an integer N (1 <= N <= 10) indicating the number of the short string.
Each of the next N lines contains a short non-empty string whose length is not more than 10.
All strings in the input would not have blank and all characters are lower case.

Output
For each test case, output a string first indicating the “black hand”, and then output an integer indicating the minimal number of the operation. If there are more than one “black hand”, please output the smallest one in lexicographical order.

Sample Input

aaabbbb
2
alice
bob

Sample Output

bob 1

思路: 首先每个子串独立处理;对于每个子串,忽略母串是个环这个事实,先考虑是个链的情况。显然有个方程dp[i][j]表示母串前i位于子串前j位匹配且母串结尾没有多余字符,最少需要的处理次数。
1.s1[i]==s2[j],dp[i][j]=min{dp[i-1][j-1],dp[i-1][j]+1,dp[i][j-1]+1};
2.s1[i]!=s2[j],dp[i][j]=min{dp[i-1][j-1]+1,dp[i-1][j]+1,dp[i][j-1]+1};

下面考虑环的问题。
首先想到将母串s复制一遍粘贴到后面,然后dp,这是对的,但是不完整。
考虑这样一个母串abcd,子串abcda,最少需要操作次数是1,但是将abcd复制后变成abcdabcd,发现没?abcda成了子串,答案变成了0。
也就是一个位置的字符被用了两次。分析一下会发现当这种情况发生时,相当于跨越了长度大于len(母串长)的一个范围,而由于答案不大于子串长度L[i],即不可能由大于L[i]*2的母串的substring生成,亦即当len>L[i]*2时,此情况不会发生,此时只需要复制前L[i]*2个字符粘贴到母串后即可。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=200050;

int dp[15][maxn];
char s1[maxn],s2[15][20];
int N,len1,len2,anscnt,ans;

int DP(char *s1,char *s2,int l1,int l2){
    int cnt=INF;
    for(int i=1;i<=l1;i++){
        dp[i][0]=i;
        for(int j=1;j<=l2;j++){
            if(s1[i-1]==s2[j-1]){
                dp[i][j]=min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1])+1);
            } else {
                dp[i][j]=min(dp[i-1][j-1]+1,min(dp[i-1][j],dp[i][j-1])+1);
            }
            if(i==l1)cnt=min(cnt,dp[i][j]);
        }
    }
    return cnt;
}
int main(){
    while(scanf("%s",s1)!=EOF){
        scanf("%d",&N);
        len1=strlen(s1);
        anscnt=INF;
        for(int j=0;jscanf("%s",s2[j]);
            len2=strlen(s2[j]);
            int cnt=INF;
            if(len1for(int i=0;ielse {
                for(int i=0;i<20;i++){
                    s1[len1++]=s1[i];
                }
                cnt=min(cnt,DP(s2[j],s1,len2,len1));
            }
            if(cntstrcmp(s2[j],s2[ans])<0)){
                ans=j;
                anscnt=cnt;
            }
        }
        printf("%s %d\n",s2[ans],anscnt);
    }
    return 0;
}

Spy’s Work

Problem Description
I’m a manager of a large trading company, called ACM, and responsible for the market research. Recently, another trading company, called ICPC, is set up suddenly. It’s obvious that we are the competitor to each other now!
To get some information about ICPC, I have learned a lot about it. ICPC has N staffs now (numbered from 1 to N, and boss is 1), and anybody has at most one superior. To increase the efficiency of the whole company, the company contains N departments and the ith department is led by the ith staff. All subordinates of the ith staff are also belong to the ith department.
Last week, we hire a spy stealing into ICPC to get some information about salaries of staffs. Not getting the detail about each one, the spy only gets some information about some departments: the sum of the salaries of staff s working for the ith department is less than (more than or equal to) w. Although the some inaccurate information, we can also get some important intelligence from it.
Now I only concerned about whether the spy is telling a lie to us, that is to say, there will be some conflicts in the information. So I invite you, the talented programmer, to help me check the correction of the information. Pay attention, my dear friend, each staff of ICPC will always get a salary even if it just 1 dollar!

Input
There are multiple test cases.
The first line is an integer N. (1 <= N <= 10,000)
Each line i from 2 to N lines contains an integer x indicating the xth staff is the ith staff’s superior(x

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=10010;
const int INF=0x3f3f3f3f;
int N,M;
int tot,head[maxn];
int vis[maxn],dis[maxn],pre[maxn];
int minv[maxn],maxv[maxn];
struct node{
    int v,next;
}edge[maxn*2];
bool flag;
void init(){
    tot=0;
    memset(head,-1,sizeof(head));
}

void add_edge(int u,int v){
    edge[tot].v=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}

void dfs(int u,int fa){
    if(!flag)return;
    if(minv[u]>maxv[u]){
        flag=false;
        return;
    }
    int sum=1;
    bool is_leaf=true;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(v==fa)continue;
        is_leaf=false;
        dfs(v,u);
        sum+=minv[v];
    }
    //if(is_leaf)return ;
    minv[u]=max(sum,minv[u]);
    if(minv[u]>maxv[u]){
        flag=false;
        return;
    }
}
int main(){
    char op[5];
    int u,v,t;
    while(scanf("%d",&N)!=EOF){
        init();
        for(int i=2;i<=N;i++){
            scanf("%d",&u);
            add_edge(u,i);
        }
        for(int i=1;i<=N;i++){
            minv[i]=1;
            maxv[i]=INF;
        }
        scanf("%d",&M);
        for(int i=0;iscanf("%d%s%d",&u,op,&t);
            if(op[0]=='<')maxv[u]=t-1;
            else if(op[0]=='>')minv[u]=t+1;
            else minv[u]=maxv[u]=t;
        }
        flag=true;
        dfs(1,0);
        printf("%s\n",flag?"True":"Lie");
    }
    return 0;
}

The Ghost Blows Light

Problem Description

My name is Hu Bayi, robing an ancient tomb in Tibet. The tomb consists of N rooms (numbered from 1 to N) which are connected by some roads (pass each road should cost some time). There is exactly one route between any two rooms, and each room contains some treasures. Now I am located at the 1st room and the exit is located at the Nth room.
Suddenly, alert occurred! The tomb will topple down in T minutes, and I should reach exit room in T minutes. Human beings die in pursuit of wealth, and birds die in pursuit of food! Although it is life-threatening time, I also want to get treasure out as much as possible. Now I wonder the maximum number of treasures I can take out in T minutes.

Input
There are multiple test cases.
The first line contains two integer N and T. (1 <= n <= 100, 0 <= T <= 500)
Each of the next N - 1 lines contains three integers a, b, and t indicating there is a road between a and b which costs t minutes. (1<=a<=n, 1<=b<=n, a!=b, 0 <= t <= 100)
The last line contains N integers, which Ai indicating the number of treasure in the ith room. (0 <= Ai <= 100)

Output
For each test case, output an integer indicating the maximum number of treasures I can take out in T minutes; if I cannot get out of the tomb, please output “Human beings die in pursuit of wealth, and birds die in pursuit of food!”.

Sample Input

5 10
1 2 2
2 3 2
2 5 3
3 4 3
1 2 3 4 5

Sample Output

11

题意:给你一棵树,每个节点有权值,每条边有话费,问从1到n在不超过T的花费内,做多得到多少全职
思路:SPFA求出最短路,然后树形dp求一边,其实就是树上的背包

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=510;
const int INF=0x3f3f3f3f;
int N,M;
int tot,head[maxn];
int vis[maxn],dis[maxn],pre[maxn];
int dp[maxn][maxn];
int a[maxn];
struct node{
    int v,next,t;
}edge[maxn*2];
void init(){
    tot=0;
    memset(head,-1,sizeof(head));
}

void add_edge(int u,int v,int t){
    edge[tot].v=v;
    edge[tot].next=head[u];
    edge[tot].t=t;
    head[u]=tot++;
}
int p[maxn];
void SPFA(int s){
    for(int i=0;i<=N;i++){
        dis[i]=INF;
        vis[i]=0;
    }
    dis[s]=0;
    vis[s]=1;
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        int u=q.front();q.pop();
        vis[u]=0;
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            if(dis[v]>dis[u]+edge[i].t){
                dis[v]=dis[u]+edge[i].t;
                pre[v]=i;
                p[v]=u;
                if(!vis[v]){
                    q.push(v);
                    vis[v]=1;
                }
            }
        }
    }
    for(int i=0;i2;
    }

    for(int i=N;i!=1;i=p[i]){
        edge[pre[i]].t=0;
        edge[pre[i]^1].t=0;
    }

}
void dfs(int u,int fa){
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        int w=edge[i].t;
        if(v==fa)continue;
        dfs(v,u);

        for(int j=M;j>=w;j--){
            for(int k=j-w;k>=0;k--){
                dp[u][j]=max(dp[u][j],dp[u][k]+dp[v][j-k-w]);
            }
        }
    }
    for(int i=0;i<=M;i++){
        dp[u][i]+=a[u];
    }
}
int main(){
    int u,v,t;
    while(scanf("%d%d",&N,&M)!=EOF){
        init();
        for(int i=1;iscanf("%d%d%d",&u,&v,&t);
            add_edge(u,v,t);
            add_edge(v,u,t);
        }
        for(int i=1;i<=N;i++){
            scanf("%d",&a[i]);
        }
        SPFA(1);
        if(dis[N]>M){
            printf("Human beings die in pursuit of wealth, and birds die in pursuit of food!\n");
            continue;
        }
        memset(dp,0,sizeof(dp));
        M-=dis[N];
        dfs(1,0);
        printf("%d\n",dp[1][M]);
    }
    return 0;
}

你可能感兴趣的:(树状数组/线段树,DFS,动态规划)