2007 Asia Regional Chengdu(树状数组神题+割点暴力+STL)

来源:互联网 发布:linux shell 当前目录 编辑:程序博客网 时间:2024/06/13 07:42

A Sequence of Numbers

Problem Description

You are given a sequence of N integers (each within the range [0, 216 - 1] ) along with P operations and in order to solve this problem you need to process the operations instructed as follows.

There are two kinds of operations that you will be instructed to perform:

1) Modification
- Given a non-negative number T , you need to increase the value of every number in the sequence by T . If the value of any number in the sequence is larger than 216 - 1 after the operation, you should divide its value by 216and take the remainder as its value;
2) Query
- Given a non-negative number T , query how many numbers in the sequence satisfies the condition that its bitwise and result with 2Tis greater than zero.

For simplicity, all you need to do here is to output the sum ( sum < 10, 000, 000, 000 ) of the answers to all queries.

Input

There are multiple test cases in the input file. Each test case starts with one integer N (N<=105) , the number of integers in the sequence. The following N line consists of one integer P (0<=P<=216 - 1) , the value on i -th line being the value of the i -th number in the sequence.

Each of the following lines is either of the format C delta " (0<=delta) , meaning that you should increase the value of every number by delta, orQ i ” (0<=i<=15) , meaning that you should calculate the answer to the query (as explained in the problem description). Every test case ends with one character `E’ on a single line, followed by a blank line.

N = - 1 indicates the end of input file and should not be processed by your program. It is guaranteed that the total number of operations in each test case does not exceed 200,000.

Output

For each test case, print the sum of answers to queries on one separate line in the format as indicated in the sample output.

Sample Input

3
1
2
4
Q 1
Q 2
C 1
Q 1
Q 2
E

-1

Sample Output

Case 1: 5

题意:给出n个数,然后给出m个操作, 操作有两种类型:
1,将所有的数都加上一个给出的数
2,求出这n个数中在二进制下第t为是1的数目有多少个(0<=t<=15)
最后输出所有询问的和。

思路:对于第一种操作, 我们记录当前已经加的和,对于第二问:
用16个树状数组, 每一个代表一个后缀。这样的话当询问的时候我们就可以利用 2^(t)<=(x+add)<2^(t+1)求出一个区间。 将区间中元素的数目加起来就行了。

#include<iostream>#include<cstdio>#include<string>#include<cstring>#include<vector>#include<cmath>#include<queue>#include<stack>#include<map>#include<set>#include<algorithm>using namespace std;typedef long long LL;const int maxn=(1<<16)+5;const int maxm=1010;const int MOD=1e9+7;const int INF=0x3f3f3f3f;int N;struct BIT{    int tree[16][maxn];    void clear(){        memset(tree,0,sizeof(tree));    }    int lowbit(int x){        return x&(-x);    }    void add(int b,int x,int val){        while(x<maxn){            tree[b][x]+=val;            x+=lowbit(x);        }    }    int query(int b,int x){        int sum=0;        while(x){            sum+=tree[b][x];            x-=lowbit(x);        }        return sum;    }}bit;int main(){    int x,cas=1;    while(scanf("%d",&N)!=EOF,N!=-1){        bit.clear();        for(int i=1;i<=N;i++){            scanf("%d",&x);            for(int j=0;j<16;j++){                bit.add(j,(x%(1<<(j+1)))+1,1);            }        }        int add=0;        LL ans=0;        char op[5];        while(scanf("%s",op)!=EOF){            if(op[0]=='E')break;            scanf("%d",&x);            if(op[0]=='C'){                (add+=x)%+(1<<16);            } else {                int tmp=add%(1<<(x+1));                int l=((1<<x)-tmp+(1<<(x+1)))%(1<<(x+1));                int r=((1<<(x+1))-1-tmp+(1<<(x+1)))%(1<<(x+1));                if(l<=r){                    ans+=bit.query(x,r+1)-bit.query(x,l);                } else {                    ans+=bit.query(x,maxn-1)-bit.query(x,l)+bit.query(x,r+1);                }            }        }        printf("Case %d: %I64d\n",cas++,ans);    }    return 0;}

Boonie and Clyde

Problem Description

As two icons of the Great Depression, Bonnie and Clyde represent the ultimate criminal couple. Stories were written, headlines captured, and films were made about the two bank robbers known as Romeo and Juliet in a getaway car.

The new generation of Bonnie and Clyde is no longer cold-blooded killers with guns. Due to the boom of internet, they turn to online banks and scheme to hack the safety system. The safety system consists of a number of computers connected by bidirectional cables. Since time is limited, they decide that they will attack exactly two computers A and B in the network, and as a result, other computers won’t be able to transmit messages via A and B . The attack is considered successful if there are at least two computers (other than A and B ) that disconnected after the attack.

As they want to minimize the risk of being captured, they need to find the easiest way to destroy the safety system. However, a brief study of the network indicates that there are many ways to achieve their objective; therefore they kidnapped the computer expert, you, to help with the calculation. To simplify the problem, you are only asked to tell them how many ways there are to destroy the safety system.

Input

There are multiple test cases in the input file. Each test case starts with two integers N (3<=N<=1000) and M (0<=M<=10000) , followed by M lines describing the connections between the N computers. Each line contains two integers A , B (1<=A, B<=N) , which indicates that computer A and B are connected by a bidirectional cable.

There is a blank line between two successive test cases. A single line with N = 0 and M = 0 indicates the end of input file.

Output

For each test case, output one integer number representing the ways to destroy the safety system in the format as indicated in the sample output.

Sample Input

4 4
1 2
2 3
3 4
4 1

7 9
1 2
1 3
2 3
3 4
3 5
4 5
5 6
5 7
6 7

0 0

Sample Output

Case 1: 2
Case 2: 11
转子:hdu 3671 Boonie and Clyde
题意:给一个无向图,要求毁掉两个点,使图变得不连通,图一开始是连通的

因为要毁掉两个点,就不是简单的求割点,再看看数据范围,点数为1000,边数为10000,Tarjan的时间复杂度为O(E),如果用枚举法,先枚举要毁掉的第一个点,再用Tarjan进行处理来找割点会不会超时呢?答案是不会,时间为O(v*E),刚好是千万级别,不超

做法:先枚举要删除的第1个点,在原图中删除它,看看删除它后整个图的变化

    1.整个图变得不连通了(即这个点本身是割点),但是还没完要分类讨论一下    (1).整个图变为两部分,但是两部分刚好都是一个点,那么这两个点再毁掉哪个点都好,图的连通分支数都不会增加,这是一个特殊情况

      例如,(1,2)(2,3)这种图,是无解的,任意毁掉两个点都无法增加图的连通分支,所以方案数为0

    (2).整个图分为两部分,但是有一部分的点数为1,另一部分大于1,那么这时候只要在较大的那部分,任意毁掉一个点(无论是不是割点都行),最后整个图都会至少被分为了两个部分            (如果毁掉的是割点,将分成更多份),所以这样产生的方案数是V-2    (3).整个图分为了两个部分,两个部分的点数都大于1,那么任意在哪个部分毁掉那个点都可以(无论是不是割点都行),最后整个图都会至少分为两个部分,所以方案数为V-1    (4).整个图被分为了三个或更多的部分,那么也是在剩下的点中任意毁掉一个点都可以(无论那个点是不是割点),方案数为V-1          (如果这个点刚好处于一个部分且这个部分只有它自己一个点,那么    毁掉后整个图的分支数减1;如果这个点在一个部分且这个部分不止它一个点且这个点不是割点,那么分支数  不会增加,如果是割点分支数为增加) 2.删除第一个点后,整个图还是连通的(是连通,不是双连通)    那么就在剩下的图中找割点,找到几个,方案数就是多少
#include<iostream>#include<cstdio>#include<string>#include<cstring>#include<vector>#include<cmath>#include<queue>#include<stack>#include<map>#include<set>#include<algorithm>using namespace std;typedef long long LL;const int maxn=1010;const int maxm=1010;const int MOD=1e9+7;const int INF=0x3f3f3f3f;vector<int> G[maxn];int N,M;int low[maxn],pre[maxn];int dfs_clock,cut_num;bool iscut[maxn];int cur_cut;void init(){    memset(pre,0,sizeof(pre));    memset(iscut,0,sizeof(iscut));    dfs_clock=cut_num=0;}int num;void dfs(int u,int fa){    num++;    low[u]=pre[u]=++dfs_clock;    int child=0;    for(int i=0;i<G[u].size();i++){        int v=G[u][i];        if(v==cur_cut)continue;        if(!pre[v]){            child++;            dfs(v,u);            low[u]=min(low[v],low[u]);            if(low[v]>=pre[u])                iscut[u]=true,cut_num++;        }        else if(pre[v]<pre[u]&&v!=fa)            low[u]=min(low[u],pre[v]);    }    if(fa<0&&child==1)iscut[u]=false,cut_num--;}void solve(){    int ans=0;    for(int i=1;i<=N;i++){        cur_cut=i;        int cnt=0,one=0;        init();        for(int j=1;j<=N;j++){            if(j!=i&&!pre[j]){                num=0;                dfs(j,-1);                cnt++;                if(num==1)one++;            }        }        if(cnt>=3)ans+=N-1;        else if(cnt>=2&&one==0)ans+=N-1;        else if(cnt>=2&&one==1)ans+=N-2;        else if(cnt==1)ans+=cut_num;    }    printf("%d\n",ans/2);}int main(){    int u,v,cas=1;    while(scanf("%d%d",&N,&M)!=EOF,N+M){        for(int i=0;i<=N;i++)G[i].clear();        for(int i=0;i<M;i++){            scanf("%d%d",&u,&v);            G[u].push_back(v);            G[v].push_back(u);        }        printf("Case %d: ",cas++);        solve();    }    return 0;}

David Shopping

For example:

David’s pocket has the capacity only for two gifts. There are 5 shops in the street, and each shop sells only one type of gift. The selling sequence of gifts is 1, 2, 1, 3, and 1.

In shop 1, the pocket is empty, so he will buy gift 1, write a number `1’ on this gift, and then put it into his pocket.

When he comes to shop 2, there is one place left in his pocket, so he buys gift 2, write a number `1’ on it, and then put it into the pocket.

When walking into shop 3, he has already got gift 1 in his pocket, so he will replace the number L (here, L = 1 ) with L + 1 .

When David visits shop 4, the pocket is full, but without gift 3 in it, so he has to discard one gift to release a place for gift 3. The number L on gift 1 is 2', but the number L on gift 2 is1’, so he will discard gift 1, write number 1 on gift 3 and then put it into the pocket.

In shop 5, the pocket is full, gift 1 is not in it, he should will discard a gift to find place for gift 1. The number L on gift 2 is 1', the number L on gift 3 is also1’. They have the same L , but gift 2 put into the pocket earlier than gift 3. So he discards gift 2, write number 1' on gift 1 and then put it into the pocket. At the end of the street, David gets two gifts in his pocket, number1’ on gift 3 and number `1’ on gift 1. The number of discarded gifts is 2.

Sample Input

3 5
1 2 3 2 4
2 4
1 2 2 1
2 6
1 2 2 1 1024 1
2 10
1 2 3 2 4 2 3 6 7 8
2 1
1048575
6 16
10 1 2 3 4 5 6 1 2 3 6 5 4 10 1 6
0 0

Sample Output

Case 1: 1
Case 2: 0
Case 3: 2
Case 4: 7
Case 5: 0
Case 6: 3

思路:刚开始用优先队列写的,后来发现优先队列并不能实时更新,想想也是
然后改成map,键值是结构体,保存x的出现次数和最早出现时间,那么如果当前的x没出现过:1. 如果满了,那么弹出map最后一个元素(因为已经排好序),然后把当前这个插入,2.没满,插入。
如果出现过,那么可以根据这个数以前出现的cnt和time,在map中找到这个店,然后删除原来的,插入现在的,相当于更新

#include<iostream>#include<cstdio>#include<string>#include<cstring>#include<vector>#include<cmath>#include<queue>#include<stack>#include<map>#include<set>#include<algorithm>using namespace std;typedef long long LL;const int maxn=100010;const int maxm=1010;const int MOD=1e9+7;const int INF=0x3f3f3f3f;int M,N;struct Node{    int cnt,t;    Node(){}    Node(int _cnt,int _t):cnt(_cnt),t(_t){}    bool operator<(const Node &a)const{        if(cnt==a.cnt)return t>a.t;        return cnt<a.cnt;    }};map<Node,int> st;map<Node,int>::iterator it;int tim[(1<<20)+10],cnt[(1<<20)+10];int a[maxn];int main(){    int cas=1;    while(scanf("%d%d",&M,&N)!=EOF,N+M){        for(int i=1;i<=N;i++){            scanf("%d",&a[i]);        }        st.clear();        memset(tim,-1,sizeof(tim));        memset(cnt,0,sizeof(cnt));        int ans=0;        for(int i=1;i<=N;i++){            if(cnt[a[i]]==0){                tim[a[i]]=i;                cnt[a[i]]++;                if(st.size()>=M){                    it=st.end();                    it--;                    cnt[it->second]=0;                    st.erase(it);                    st[Node(1,i)]=a[i];                    ans++;                } else {                    st[Node(1,i)]=a[i];                }            } else {                it=st.find(Node(cnt[a[i]],tim[a[i]]));                st.erase(it);                cnt[a[i]]++;                st[Node(cnt[a[i]],tim[a[i]])]=a[i];            }        }        printf("Case %d: %d\n",cas++,ans);    }    return 0;}
0 0
原创粉丝点击