2015 8月 做题记录

1 HDU 4334(线性查找|暴力hash)

题目:给出五个集合,每个集合元素个数为n,现问是否有五个元素分别属于这五个集合且其和为0.

思路:给定两个排好序的序列,可以在线性时间内求出是否有两个数分别属于这两个集合且其和为一定值。方法是设置两个指针,线性扫描两个数组。本题可以把5个集合分为三组,枚举其中一组,在另外两组中线性查找。总时间复杂度应该是O(n^3).

#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
#define pb push_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.0000000001
#define IINF (1<<30)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int maxv=205;
ll s[5][205];
ll g1[maxv*maxv],g2[maxv*maxv];
ll *g3=s[4];
int T,n;
int main(){
    /////freopen("/home/files/CppFiles/in","r",stdin);
    /*    std::ios::sync_with_stdio(false);
        std::cin.tie(0);*/
    cin>>T;
    while(T--){
        cin>>n;
        for(int i=0;i<5;i++){
            for(int j=0;j<n;j++){
                scanf("%lld",&s[i][j]);
            }
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                g1[i*n+j]=s[0][i]+s[1][j];
                g2[i*n+j]=s[2][i]+s[3][j];
            }
        }
        sort(g1,g1+n*n);
        sort(g2,g2+n*n);
        sort(g3,g3+n);
        int h1=0,h2=n*n-1;
        bool f=0;
        for(int i=n-1;i>=0;i--){
            ll tar=-g3[i];
            int h1=0,h2=n*n-1;
            while(1){
                while(h1<n*n&&g1[h1]+g2[h2]<tar){
                    h1++;
                }
                while(h2>=0&&g1[h1]+g2[h2]>tar){
                    h2--;
                }
                if(h1>=n*n||h2<0) break;
                if(g1[h1]+g2[h2]==tar){
                    f=1;
                    break;
                }
            }
        }
        if(f) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    return 0;
}
View Code

 

hash: 暴力建hash表,然后查找,之前没写过hash表结果当场写sb了...=_=||。

#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
#define pb push_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.0000000001
#define IINF (1<<30)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int mod=1234567;
const ll oo=100700010001000100LL;
ll hashset[mod];
void init(){
    for(int i=0;i<mod;i++) hashset[i]=oo;
}
int get(ll x){
    return (x%mod+mod)%mod;
}
void insert(ll x){
    int idx=(x%mod+mod)%mod;
    while(hashset[idx]!=oo){
        if(hashset[idx]==x) return;
        idx++;
        if(idx==mod) idx=0;
    }
    hashset[idx]=x;
}
bool find(ll x){
    int idx=(x%mod+mod)%mod;
    while(hashset[idx]!=oo&&hashset[idx]!=x){
        idx++;
//        if(get(hashset[idx])!=get(x)) return 0;
        if(idx==mod) idx=0;
    }
    return hashset[idx]==x;
}
ll s[5][205];
int T,n;
int main(){
    /////freopen("/home/files/CppFiles/in","r",stdin);
    /*    std::ios::sync_with_stdio(false);
        std::cin.tie(0);*/
    cin>>T;
    while(T--){
        cin>>n;
        for(int i=0;i<5;i++){
            for(int j=0;j<n;j++){
                scanf("%lld",&s[i][j]);
            }
        }
        init();
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                insert(s[0][i]+s[1][j]);
            }
        }
        bool f=0;
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                for(int k=0;k<n;k++){
                    if(find(-s[2][i]-s[3][j]-s[4][k])){
                        f=1;
                        goto lab;
                    }
                }
            }
        }
lab:
        if(f) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    return 0;
}
View Code

 2 HDU 4355(三分求极值

题目:每个人到某点的距离为直线距离的立方,现在求直线上一点到其他点的距离之和最小。

思路:裸的三分算法应用。

#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
#define pb push_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define cu(x) (x)*(x)*(x)
#define eps 0.0000000001
#define IINF (1<<30)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int maxv=5e4+300;
int T,N;
double x[maxv],w[maxv];
double cul(double y){
    double ans=0;
    for(int i=0;i<N;i++){
        ans+=w[i]*cu(abs(y-x[i]));
    }
    //cout<<ans<<endl;
    return ans;
}
double work(){
    double l=-1e6,r=1e6;
    double ansl=cul(l),ansr=cul(r);
    while(r-l>0.0000001){
    //    cout<<l<<" "<<r<<endl;
        double mid=(l+r)/2;
        double ansm=cul(mid);
        double lmid=(mid+l)/2;
        double rmid=(r+mid)/2;
        double anslm=cul(lmid);
        double ansrm=cul(rmid);
        if(anslm<ansm){
            r=mid;
            ansr=ansm;
        }else{
            l=lmid;
            ansl=anslm;
        }
        if(ansrm<ansm){
            l=mid;
            ansl=ansm;
        }else{
            r=rmid;
            ansr=ansrm;
        }
    }
    return l;
}
int cas=0;
int main(){
    //freopen("/home/files/CppFiles/in","r",stdin);
    /*    std::ios::sync_with_stdio(false);
        std::cin.tie(0);*/
    cin>>T;
    while(T--){
        cin>>N;
        for(int i=0;i<N;i++){
            scanf("%lf%lf",&x[i],&w[i]);
        }
//        cout<<cul(0)<<endl;
        printf("Case #%d: %.0f\n",++cas,cul(work()));
    }
    return 0;
}
View Code

 3 HDU 4337(dfs

 题目:求出一个图的哈密顿回路,保证每个点的出度大于点数的一半。

思路:感觉dfs肯定会tle但是大家基本都是dfs水过的,一种可能是数据太水,一种可能是由于出度的限制(这种图的哈密顿回路是一定存在的),dfs一定可以迅速出解,具体什么数据可以卡掉dfs还没想出,可能真的卡不掉吧。。。哈密顿回路的算法是O(n^2)的,并且也需要这个稠密图的条件。

PS:我dfs居然写挂了。。。还没搞清楚究竟挂在哪里了。。。。

#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
#define pb push_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.0000000001
#define IINF (1<<30)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int maxv=160;
bool G[maxv][maxv];
int N,M;
bool m[maxv];
bool dfs(int v,int num){
    if(num==N){
        if(G[v][1]){
            return 1;
        }
        else
            return 0;
    }
    for(int i=1;i<=N;i++){
        if(!m[i]&&G[v][i]){
            m[i]=1;
            if(dfs(i,num+1)){
                printf("%d ",i);
                return 1;
            }
            m[i]=0;
        }
    }
    return 0;
}
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    /*    std::ios::sync_with_stdio(false);
        std::cin.tie(0);*/
    while(cin>>N>>M){
        memset(G,0,sizeof G);
        for(int i=0;i<M;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            G[x][y]=G[y][x]=1;
        }
        memset(m,0,sizeof m);
        m[1]=1;
        if(dfs(1,1)){
            printf("%d\n",1);
        }else{
            puts("no solution");
        }
    }
    return 0;
}
View Code

 4 HDU 4357(找规律

题目:一个字符串可以做如下变换,取两个字符交换位置并且字符变大一位,现在给出两个字符串,问是否可以由某种变换方式使两个字符串一致。

思路:本来是按照cf的一道题的思路来想的,这个变换满足等价关系的定义,因为任何变换可以在做26次之后返回初始状态,所以是可逆的。然后就是要找到一个标准把两个字符串变成一样的(比如字典序最小),但是直接贪心变换似乎不能直接得到字典序最大或最小的等价串。然后就卡住了。。。其实想想也是当然的,这种题目的重点是如何变换,发现这种变换的某种普遍性质才是解题的关键。。。那么这个题目中的变换有什么特点呢?嗯。。比较棘手的一点是这个字符变换的结果是与顺序有关的,所以不能直接交换字符,这时候就需要YY一下(但是我并没有YY出来。。郁闷。。)。。。首先我们应该容易注意到奇偶性的特点,每次变换字符串整体的和加2,也就是总和的奇偶性不变,并且如果奇数字符有偶数个那么不管怎么变,奇数字符个数的奇偶性是不会变的。这时候一个猜想就是奇数字符个数的奇偶性直接决定了字符串的等价性。(以下是队友的分析),首先奇偶性不同的一定不等价,如果奇偶性相同,那么可以先进行变换使得奇数字符的个数也相同(这时候同时保证奇対奇,偶对偶),然后把最后一位的字符当做中转,把前面的奇偶字符全部换成相等(因为每两次变换,字符的值增加2,所以必能相等),现在剩下作为中转的字符可能不等,那么我们需要一种方法,在不改变其他字符的情况下,把最后两个不等的字符变相等。考虑三个字符abc,做交换cba,bca,cba,abc,这四个交换后字符c的值增加4,而ab我们可以通过24次交换变回原值,由于有26个字符的取模作用,实际上变换7次,c增值28相当于c增值2,而最后两个字符奇偶相同,那么必然可以变成相同,证闭。

关于顺序,对于三个数,通过把一对数交换13次,再把另一对数交换13次,再交换13次使三个数变回原值,可以达到任意互换位置的效果。

详见:http://blog.csdn.net/acdreamers/article/details/9390907

#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
#define pb push_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.0000000001
#define IINF (1<<30)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
int T;
string a,b;
int cas=0;
void change(char &a,char &b){
    int a1=a-'a',b1=b-'a';
    a1=(a1+1)%26,b1=(b1+1)%26;
    a=b1+'a',b=a1+'a';
}
int main(){
    /////freopen("/home/files/CppFiles/in","r",stdin);
    /*    std::ios::sync_with_stdio(false);
        std::cin.tie(0);*/
    cin>>T;
    while(T--){
        cin>>a>>b;
        int len=a.size();
        bool f=0;
        if(len==2){
            for(int i=0;i<30;i++){
                if(a==b) f=1;
                change(a[0],a[1]);
            }
        }else{
            int cnt1=0,cnt2=0;
            for(int i=0;i<len;i++){
                if(a[i]%2) cnt1++;
                if(b[i]%2) cnt2++;
            }
            if(cnt1%2==cnt2%2) f=1;
        }
        if(!f) printf("Case #%d: NO\n",++cas);
        else printf("Case #%d: YES\n",++cas);

    }
    return 0;
}
View Code

HDU 4349(规律

题目:问n的组合数中奇数有多少个。

思路:找规律水过,看题解说是lucas定理,没仔细看,暂且记下。

HDU 4359(dp

题目:某个树的节点都是2的1到n次幂并且互不相同,问保证所有左子树的节点和大于右子树的树共有多少种。

思路:dp,首先是2的幂,实际上不论节点集合的权值如何,集合中权值最大得点属于哪个子树是已经确定的,然后就是dp的状态,是节点数为n、的时候深度小于等于d的子树有多少种,题目中的节点虽然看似都不同,但是子树的个数与节点值并无关系(就结论而言。。。。)

#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
#define pb push_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.0000000001
#define IINF (1<<30)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int maxv=370;
const ll mod=1e9+7;
ll C[maxv][maxv];
ll dp[maxv][maxv];
int N,D;
int cas=0;
void init(){
    memset(C,0,sizeof C);
    for(int i=0;i<maxv;i++){
        C[i][0]=1,C[i][i]=1;
    }
    for(int i=1;i<=maxv;i++){
        for(int j=1;j<=i;j++){
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
        }
    }
    memset(dp,0,sizeof dp);
    for(int i=1;i<maxv;i++) dp[1][i]=1;
    for(int i=2;i<maxv;i++) dp[2][i]=4;
    for(int i=3;i<maxv;i++){
        for(int j=1;j<maxv;j++){
            for(int k=1;k<i-1;k++){
                dp[i][j]=(dp[i][j]+i*dp[k][j-1]%mod*C[i-2][k]%mod*dp[i-1-k][j-1]%mod)%mod;
            }
            dp[i][j]=(dp[i][j]+i*dp[i-1][j-1]*2%mod)%mod;
        }
    }
}
int main(){
    ////freopen("/home/files/CppFiles/in","r",stdin);
    /*    std::ios::sync_with_stdio(false);
        std::cin.tie(0);*/
    init();
    int T;
    cin>>T;
    while(T--){
        cin>>N>>D;
        printf("Case #%d: %lld\n",++cas,((dp[N][D]-dp[N][D-1])%mod+mod)%mod);
    }
    return 0;
}
View Code

 7 HDU 4345(数学,背包

题目:每个置换都有一个最小循环节,现要求对于长度为n的置换循环节的长度有多少种。

思路:虽然看起来是和置换群有关,但是实际上由于最小循环节等于置换中所有环的最小公倍数,问题就变成了求出和为n的若干正整数的最小公倍数有多少种。又因为任意个数的1不影响结果且不使用质数而使用不同质数组成合数不可能增加最小公倍数的个数,故问题转变为质数和其幂相加小于n的方案有多少种。这是个背包问题,要求统计种数,参考了kuangbin大神的记忆化搜索实现。。。。t了n发结果最后发现是因为质数筛的范围略小。。。幸亏数据范围小可以在本机都试一遍。。。。另外。。dp的初值也想得有点问题。。

#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
#define pb push_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.0000000001
#define IINF (1<<30)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int maxv=1300;
ll dp[maxv][maxv];
int h=0;
int pri[maxv];
bool m[maxv];
int N;
void init(){
    memset(m,0,sizeof m);
    for(int i=2;i<maxv;i++){
        if(!m[i]){
            pri[h++]=i;
        }
        for(int j=0;j<h&&i*pri[j]<maxv;j++){
            m[i*pri[j]]=1;
            if(i%pri[j]==0) break;
        }
    }
    memset(dp,-1,sizeof dp);
    return;
}
ll dfs(int n,int p){
    if(dp[n][p]!=-1) return dp[n][p];
    if(pri[p]>n) return dp[n][p]=1;
    dp[n][p]=0;
    for(int i=pri[p];i<=n;i*=pri[p]){
        dp[n][p]+=dfs(n-i,p+1);
    }
    dp[n][p]+=dfs(n,p+1);
    return dp[n][p];
}
int main(){
    ///freopen("/home/files/CppFiles/in","r",stdin);
    //freopen("/home/files/CppFiles/out","w",stdout);
    /*    std::ios::sync_with_stdio(false);
        std::cin.tie(0);*/
    init();
    while(scanf("%d",&N)!=EOF){
        printf("%lld\n",dfs(N,0));
    }
    return 0;
}
View Code

 8 CF 547D(欧拉回路

题目:给出若干个坐标,现在把每个点染成红蓝两色,要求每一行和每一列的红色和蓝色点数相差不超过1.

思路:首先,此题必然有解,如果将每一行和每一列分别作为点构图,每一个点对应一条边的话,染色这个点就对应与染色这条边,而对于图中的环来说,只要交替染色,就能保证环中的点差为0,对于剩下的无环图,交替染色,差最大为1.。然后问题在于如何求解,每次找环然后删的话效率比较低也不好实现。。。cf的官方题解是这样做的:对于出度为奇的点,删去一条临边,让然后递归求解,所有点的度都为偶数的时候,对每一点求欧拉回路,求的时候直接删边,把经过的点存下以便标记。。。对于删掉的边,回溯的时候根据两边的点的度判断染成什么颜色。。。所有的维护奇数偶数度的点,以及边的染色和删边都可以用set和map实现,处理的时候可以lazy操作,在需要用边的时候再删掉标记删掉的边。

ps:想清楚为啥删掉的边只根据一点直接染色?依照这种方法,每次删掉一条链,删完之后链两端的度会差一,由于不会有两条链共端点,所以显然符合要求。

(代码基本是完全照抄cf标程的,自己完全不会实现。。。)

#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
#define pb push_back
#define PB pop_back
#define fs first
#define se second
#define sq(x) (x)*(x)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int maxv=2e5+3000;
vector<int> G[maxv*2];
set<int> D[2];
map<P,bool> del,mp;
bool deg[maxv*2];
vector<P> es;
vector<int> eu;
int x[maxv*2];
void addedge(int u,int v){
    G[v].pb(u);
    G[u].pb(v);
    es.pb(P(u,v));
}
void eular(int v){
    while(!G[v].empty()){
        int u=G[v].back();
        G[v].PB();
        if(!del[P(u,v)]){
            del[P(u,v)]=del[P(v,u)]=1;
            eular(u);
        }
    }
    eu.pb(v);
}
void deledge(int u,int v){
    del[P(u,v)]=del[P(v,u)]=1;
    D[deg[v]].erase(v);
    D[deg[u]].erase(u);
    deg[u]=!deg[u];
    deg[v]=!deg[v];
    D[deg[u]].insert(u);
    D[deg[v]].insert(v);
}
int n;
void solve(){
    if(!D[1].empty()){
        int v=*D[1].begin();
        while(!G[v].empty()&&del[P(v,G[v].back())]) G[v].PB();
        int u=G[v].back();
        deledge(v,u);
        solve();
        int c=0;
        if(x[u]>0) c=1;
        if(c==1){
            x[u]--,x[v]--;
        }else{
            x[u]++,x[v]++;
        }
        mp[P(u,v)]=mp[P(v,u)]=c;
    }else{
        for(int i=0;i<maxv*2;i++){
            eu.clear();
            eular(i);
            for(int j=0;j<eu.size()-1;j++){
                mp[P(eu[j],eu[j+1])]=mp[P(eu[j+1],eu[j])]=j%2;
            }
        }
    }
}
int main(){    
    cin>>n;
    for(int i=0;i<n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        x--,y--;
        x=2*x+1;
        y=2*y;
        addedge(x,y);
    }
    for(int i=0;i<2*maxv;i++){
        D[G[i].size()%2].insert(i),deg[i]=G[i].size()%2;
    }
    solve();
        for(int i=0;i<es.size();i++){
            printf("%c",mp[es[i]]?'r':'b');
        }
    return 0;
}
View Code

 9 PKU 1986(lca

题目:要求一棵树上任意两点之间的距离。

思路:裸的lca,用的是挑战程序设计竞赛上的倍增的方法,在二分求lca的同时算出距离。也可以使用rmq的方法做。

#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
#define IOS std::ios::sync_with_stdio(false);std::cin.tie(0)
#define pb push_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.0000000001
#define IINF (1<<30)
#define clr(x) memset((x),0,sizeof (x))
using namespace std;
const int maxv=4e4+3000;
struct EDGE{
    int t,w;
    EDGE *nt;
}pool[maxv*4];
int ph=0;
EDGE *newedge(){
    return &pool[ph++];
}
EDGE *head[maxv],*tail[maxv];
void addedge(int x,int y,int w){
    tail[x]->nt=newedge();
    tail[x]=tail[x]->nt;
    tail[x]->nt=NULL;
    tail[x]->t=y,tail[x]->w=w;
}
int n,q,s;
int par[30][maxv*2],deep[maxv*2],dis[30][maxv*2];
void clear(){
    memset(par,-1,sizeof par);
    memset(dis,-1,sizeof dis);
    ph=0;
    for(int i=0;i<maxv;i++){
        head[i]=newedge();
        head[i]->nt=NULL;
        tail[i]=head[i];
    }
}
void dfs(int v,int p,int d,int di){
    par[0][v]=p;
    dis[0][v]=di;
    deep[v]=d;
    for(EDGE *i=head[v]->nt;i!=NULL;i=i->nt){
        if(i->t==p) continue;
        dfs(i->t,v,d+1,i->w);
    }
}
void init(int v){
    dfs(1,-1,0,0);
    for(int k=0;k<25;k++){
        for(int v=1;v<=n;v++){
            if(par[k][v]<0) par[k+1][v]=-1;
            else par[k+1][v]=par[k][par[k][v]];
            dis[k+1][v]=dis[k][par[k][v]]+dis[k][v];
        }
    }
}
int lca(int u,int v){
    if(deep[u]>deep[v]) swap(u,v);
    int dd=0;
    for(int k=0;k<25;k++){
        if((deep[v]-deep[u])>>k&1){
            dd+=dis[k][v];
            v=par[k][v];
        }
    }
    if(u==v) return dd;
    for(int k=24;k>=0;k--){
        if(par[k][u]!=par[k][v]){
            dd+=dis[k][u]+dis[k][v];
            u=par[k][u];
            v=par[k][v];
        }
    }
    return dis[0][u]+dis[0][v]+dd;
}
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    while(cin>>n>>q){
        clear();
        for(int i=0;i<n-1;i++){
            int x,y,z;
            char e;
            scanf("%d%d%d %c",&x,&y,&z,&e);
            addedge(x,y,z);
            addedge(y,x,z);
        }
        dfs(1,-1,0,0);
        init(1);
        int K;
        cin>>K;
        while(K--){
            int a,b;
            scanf("%d%d",&a,&b);
            cout<<lca(a,b)<<endl;
        }
    }
    return 0;
}
View Code

 10 HDU 4372(组合数学,斯特林数

题目:n个建筑物,高度各不相同,如果后面一个的高度大于前一个就能看见,现在从前往后看能看见f个,从后往前看能看见b个,问有多少种可能的排列。

思路:两个建筑之间的建筑物高度比前一个小,那么他们的排列种数就是n-1!,恰好是n的圆排列,选出f+b个环对应第一类斯特林数。

#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
using namespace std;
#define IOS std::ios::sync_with_stdio(false);std::cin.tie(0)
#define pb push_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.0000000001
#define IINF (1<<30)
#define clr(x) memset((x),0,sizeof (x))
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> P;
const ll mod=1e9+7;
const int maxv=2000+5;
ll S[maxv][maxv];
ll C[maxv][maxv];
void init(){
    for(int i=0;i<maxv;i++){
        C[i][0]=1;
        if(i!=0) S[i][1]=1;
        S[i][i]=1;
    }
    for(int i=1;i<maxv;i++){
        for(int j=1;j<=i;j++){
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
            S[i][j]=((i-1)*S[i-1][j]+S[i-1][j-1])%mod;
        }
    }
}
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    init();
    int N,F,B;
    int T;
    cin>>T;
    while(T--){
        scanf("%d%d%d",&N,&F,&B);
        ll ans=0;
        int f=F-1,b=B-1;
        if(f+b<=N-1)
            ans=(S[N-1][f+b]*C[f+b][f])%mod;
        printf("%d\n",(int)ans);
    }
    return 0;
}
View Code

 11 CF 567C

题目:给出一个序列和k值,求有多少个三个元素且公比为k的子列。

思路:枚举中间值,然后用两个map维护前后的值,这题比较恶心的是卡ll,卡000的情况。。。wa了5发看了数据才过得。。

#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
using namespace std;
#define IOS std::ios::sync_with_stdio(false);std::cin.tie(0)
#define pb push_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.0000000001
#define IINF (1<<30)
#define clr(x) memset((x),0,sizeof (x))
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> P;
map<ll,ll> m1,m2;
const int maxv= 2e5+300;
ll n,k;
ll a[maxv];
int main(){
///    freopen("/home/files/CppFiles/in","r",stdin);
    cin>>n>>k;
    for(int i=0;i<n;i++){
        cin>>a[i];
        if(m2.find(a[i])!=m2.end()){
            m2[a[i]]++;
        }else{
            m2[a[i]]=1;
        }
    }
    ll n0=m2[0];
    ll ans=n0*(n0-1)*(n0-2)/6;
    for(int i=0;i<n;i++){
        if(m2[a[i]]==1) m2.erase(a[i]);
        else m2[a[i]]--;
        if(k==1&&a[i]!=0){
            ans+=m1[a[i]]*(m1[a[i]]-1)/2;
        }
        else if(a[i]%k==0&&a[i]!=0){
            if(m1.find(a[i]/k)!=m1.end()&&m2.find(a[i]*k)!=m2.end()){
                ans+=m1[a[i]/k]*m2[a[i]*k];
            }
        }
        m1[a[i]]++;
    }
    cout<<ans<<endl;
    return 0;
}
View Code

 12 HDU 4370(最短路

题目:给出一个矩阵,要求一个矩阵满足若干条件使结果最小(详见原题

思路:几乎看不出是最短路啊。。。。条件实际上对应1的出度为1,n的入度为1,其他点的出度等于入度。c矩阵对应边权。还有要注意两个环的情况。

#include<bits/stdc++.h>
#define pb push_back
#define se second
#define fs first
#define sq(x) (x)*(x)
#define eps 0.000000001
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int maxv=305;
int n;
int C[maxv][maxv];
ll dis[maxv];
ll dis2[maxv];
priority_queue<P,vector<P>,greater<P> > Q;
ll cyc1,cyc2;
void dij1(){
    memset(dis,0x3f,sizeof dis);
    dis[1]=0;
    ll cyc=1e18;
    Q.push(P(0,1));
    while(!Q.empty()){
        ll v=Q.top().se,d=Q.top().fs;
        Q.pop();
        if(d>dis[v]) continue;
        for(int i=1;i<=n;i++){
            if(dis[i]>dis[v]+C[v][i]){
                dis[i]=dis[v]+C[v][i];
                Q.push(P(dis[i],i));
            }
            if(i!=1){
                cyc1=min(cyc1,dis[i]+C[i][1]);
            }
        }
    }
}
void dij2(){
    memset(dis2,0x3f,sizeof dis2);
    dis2[n]=0;
    ll cyc=1e18;
    Q.push(P(0,n));
    while(!Q.empty()){
        ll v=Q.top().se,d=Q.top().fs;
        Q.pop();
        if(d>dis2[v]) continue;
        for(int i=1;i<=n;i++){
            if(dis2[i]>dis2[v]+C[v][i]){
                dis2[i]=dis2[v]+C[v][i];
                Q.push(P(dis2[i],i));
            }
            if(i!=n){
                cyc2=min(cyc2,dis2[i]+C[i][n]);
            }
        }
    }
}
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    while(cin>>n){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                scanf("%d",&C[i][j]);
            }
        }
        cyc1=cyc2=1e18;
        dij1();
        dij2();
        cout<<min(dis[n],cyc1+cyc2)<<endl;
    }
    return 0;
}
View Code

 13    HDU 5294(最短路,最小割

题目:求出s到t的最短路的最小边数,以及让最短路变长所需要删除的最小边数。

思路:前者就是最短路加点东西,后者是在最短路上的边组成的图上求最大流。最短路上的边就是d[u]==d[v]+w的边。

//#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
#define pb push_back
#define PB pop_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define IINF (1<<29)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int maxv=2e3+400;
struct edge{
    int to,cap,rev;
};
queue<int> que;
vector<edge> nG[maxv];
int level[maxv];
int iter[maxv];
void add_edge(int from,int to,int cap){
    nG[from].pb((edge){to,cap,nG[to].size()});
    nG[to].pb((edge){from,0,nG[from].size()-1});
}
void bfs(int s){
    memset(level,-1,sizeof level);
    level[s]=0;
    que.push(s);
    while(!que.empty()){
        int v=que.front();que.pop();
        for(int i=0;i<nG[v].size();i++){
            edge &e=nG[v][i];
            if(e.cap>0&&level[e.to]<0){
                level[e.to]=level[v]+1;
                que.push(e.to);
            }
        }
    }
}
int dfs(int v,int t,int f){
    if(v==t){
        return f;
    }
    for(int &i=iter[v];i<nG[v].size();i++){
        edge &e=nG[v][i];
        if(e.cap>0&&level[v]<level[e.to]){
            int d=dfs(e.to,t,min(f,e.cap));
            if(d>0){
                e.cap-=d;
                nG[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}
int dinic(int s,int t){
    int flow=0;
    for(;;){
        bfs(s);
        if(level[t]<0) return flow;
        memset(iter,0,sizeof iter);
        int f;
        while((f=dfs(s,t,IINF))>0){
            flow+=f;
        }
    }
}
struct EDGE{
    int t,d;
    EDGE(int t,int d):t(t),d(d){}
};
int n,m;
int minn[maxv];
ll dis[maxv];
vector<EDGE> G[maxv];
void clear(){
    for(int i=0;i<maxv;i++){
        G[i].clear();
        nG[i].clear();
    }
}
priority_queue<P,vector<P>,greater<P> > Q;
void dij(int s){
    memset(minn,0x3f,sizeof minn);
    memset(dis,0x3f,sizeof dis);
    dis[s]=0;
    minn[s]=0;
    Q.push(P(0,s));
    while(!Q.empty()){
        ll v=Q.top().se,d=Q.top().fs;
        Q.pop();
        if(d>dis[v]) continue;
        for(int i=0;i<G[v].size();i++){
            int u=G[v][i].t,d=G[v][i].d;
            if(dis[u]>dis[v]+d){
                dis[u]=dis[v]+d;
                minn[u]=minn[v]+1;
                Q.push(P(dis[u],u));
            }else if(dis[u]==dis[v]+d&&minn[u]>minn[v]+1){
                minn[u]=minn[v]+1;
                Q.push(P(dis[u],u));
            }
        }
    }
}
void makeG(){
    for(int i=1;i<=n;i++){
        for(int j=0;j<G[i].size();j++){
            EDGE &e=G[i][j];
            if(dis[e.t]==dis[i]+e.d){
                add_edge(i,e.t,1);
            }
        }
    }
}
int main(){    
    freopen("/home/files/CppFiles/in","r",stdin);
    //freopen("/home/files/CppFiles/out","w",stdout);
    while(cin>>n>>m){
        clear();
        for(int i=0;i<m;i++){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            G[x].pb(EDGE(y,z));
            G[y].pb(EDGE(x,z));
        }
        dij(1);
        makeG();
        int maxflow=dinic(1,n);
        int minnum=minn[n];
        cout<<maxflow<<" "<<m-minnum<<endl;
    }
    return 0;
}
View Code

 14 HDU 4374(dp,单调队列

题目:是男人就下100层!!每层可以移动t步,问经过格子的最大和是多少。

思路:利用前缀和再稍加变形可以发现每次的dp值实际上是滑窗上的最值,故可用单调队列处理。

#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
using namespace std;
#define IOS std::ios::sync_with_stdio(false);std::cin.tie(0)
#define pb push_back
#define PB pop_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.0000000001
#define IINF (1<<30)
#define clr(x) memset((x),0,sizeof (x))
typedef long long ll;
//Ŕ#define int ll
typedef pair<int,int> pii;
typedef pair<ll,ll> P;
const int maxn=105;
const int maxm=1e4+40;
int N,M,X,T;
int s[maxn][maxm];
ll dp[maxn][maxm];
ll sum[maxn][maxm];
P q1[maxm],q2[maxm];
int q1f,q1b,q2f,q2b;
void add(P *q,int &f,int &b,ll v,int o){
    if(b-f==0){
        q[b++]=P(v,o);
        return;
    }
    while(b-f>0&&q[b-1].fs<v){
        b--;
    }
    q[b++]=P(v,o);
    return;
}
ll getf(P *q,int &f,int &b,int o){
    while(b-f>0&&o-q[f].se>T){
        f++;
    }
    return q[f].fs;
}
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    while(cin>>N>>M>>X>>T){
        for(int i=1;i<=N;i++){
            for(int j=1;j<=M;j++){
                scanf("%d",&s[i][j]);
                sum[i][j]=sum[i][j-1]+s[i][j];
            }
        }
        for(int i=1;i<=M;i++){
            dp[1][i]=-1e15;
        }
        dp[1][X]=0;
        for(int i=2;i<=N+1;i++){
            q1f=q1b=q2f=q2b=0;
            for(int j=1;j<=min(T,M);j++){
                ll v=dp[i-1][j]+sum[i-1][j];
                add(q2,q2f,q2b,v,j);
            }
            for(int j=1;j<=M+1;j++){
                if(j+T<=M){
                    ll v=dp[i-1][j+T]+sum[i-1][j+T];
                    add(q2,q2f,q2b,v,j+T);
                }
                ll v=dp[i-1][j]-sum[i-1][j-1];
                if(j<=M) add(q1,q1f,q1b,v,j);
                dp[i][j]=max(getf(q1,q1f,q1b,j)+sum[i-1][j],getf(q2,q2f,q2b,j+T)-sum[i-1][j-1]);
            }
        }
        ll ans=-1e18;
        for(int i=1;i<=M;i++){
            ans=max(ans,dp[N+1][i]);
        }
        cout<<ans<<endl;
    }
    return 0;
}
View Code

 15 HDU 2256(矩阵快速幂

题目:http://acm.hdu.edu.cn/showproblem.php?pid=2256

思路:之前做过一道差不多的,今天竟然又不会。。。看来还是要多复习啊。。。。。参见这篇题解

http://www.cnblogs.com/wally/archive/2013/03/01/2939318.html

//#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
#define pb push_back
#define PB pop_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define IINF (1<<29)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const ll mod=1024;
const int sz=2;
struct matrix{
    ll m[20][20];
    matrix(){
        memset(m,0,sizeof m);
    }
    void mul(matrix b){
        matrix c;
        for(int i=0;i<sz;i++){
            for(int j=0;j<sz;j++){
                c.m[i][j]=0;
                for(int k=0;k<sz;k++){
                    c.m[i][j]=(c.m[i][j]+m[i][k]*b.m[k][j])%mod;
                }
            }
        }
        for(int i=0;i<sz;i++){
            for(int j=0;j<sz;j++){
                m[i][j]=c.m[i][j];
            }
        }
    }
};
matrix qpow(matrix x,ll p){
    matrix ans;
    for(int i=0;i<sz;i++){
        ans.m[i][i]=1;
    }
    while(p>0){
        if(p&1) ans.mul(x);
        x.mul(x);
        p>>=1;
    }
    return ans;
}
int T;
int main(){    
    freopen("/home/files/CppFiles/in","r",stdin);
    //freopen("/home/files/CppFiles/out","w",stdout);
    matrix cons;
    cons.m[0][0]=5,cons.m[0][1]=12,cons.m[1][0]=2,cons.m[1][1]=5;
    ll n;
    cin>>T;
    while(T--){
        cin>>n;
        matrix ans=qpow(cons,n-1);
        cout<<((ans.m[0][0]*5+ans.m[0][1]*2)*2-1+mod)%mod<<endl;
    }
    return 0;
}
View Code

 16 CF Gym 100625B(概率dp

题目:需要贿赂n个人,每个人需要的钱和贿赂成功的概率给出,问最后能贿赂到c个人的最大概率是多少。

思路:自己思路不清楚。。。开始想的是枚举这些钱能贿赂的人的集合,然后计算每个方案的成功概率,然而。。。枚举集合就是很高的复杂度,计算成功概率还的用容斥原理。。。简直爆炸。。。。后来发现是理解错题意了,此题有个很关键的地方是“每次贿赂完一个人后你可以立即知道是否成功,再贿赂下一个人”,显然,贿赂了足够多的人之后就不用再贿赂,所以这题的关键是顺序,故dfs的时候枚举的是“在当前可选的人中第一个选的是谁”,根据这个进行转移,同时,对于已经选过的集合,顺序无关(因为成功的还没到需要的数量),所以可以进行dp。状态是已经贿赂过的集合,还剩多少需要成功贿赂的人。

#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
using namespace std;
#define IOS std::ios::sync_with_stdio(false);std::cin.tie(0)
#define pb push_back
#define PB pop_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.00000001
#define IINF (1<<30)
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> P;
const int maxv=16;
int T,n,c,m;
int b[maxv],p[maxv];
double dp[1<<maxv][20];
int num[1<<maxv];
int cost[1<<maxv];
void clear(){
    for(int i=0;i<(1<<maxv);i++){
        for(int j=0;j<20;j++){
            dp[i][j]=-1;
        }
    }
}
double dfs(int mask,int y){
    if(dp[mask][y]!=-1) return dp[mask][y];
    if(cost[mask]>m) return dp[mask][y]=0;
    if(y==0) return 1;
    dp[mask][y]=0;
    for(int i=0;i<n;i++){
        if(mask&(1<<i)) continue;
        dp[mask][y]=max(dp[mask][y],dfs(mask|(1<<i),y-1)*p[i]/100.0+dfs(mask|(1<<i),y)*(100-p[i])/100.0);
    }
    return dp[mask][y];
}
void cul(int mask,int nu,int co,int p){
    if(p==n){
        num[mask]=nu;
        cost[mask]=co;
        return;
    }
    cul(mask^(1<<p),nu+1,co+b[p],p+1);
    cul(mask,nu,co,p+1);
}
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    cin>>T;
    while(T--){
        clear();
        cin>>n>>c>>m;
        for(int i=0;i<n;i++){
            scanf("%d%d",b+i,p+i);
        }
        cul(0,0,0,0);
        cout<<dfs(0,c)<<endl;
    }
    return 0;
}
View Code

17 CF 568B

题目:求元素数量为n的关系中,具有传递,对称,但是不自反的关系的数目。

思路;传递,对称且自反的关系是等价关系,满足前两者但不满足自反的关系只能是某些元素不存在这个关系集合中(如果它与自身相关,矛盾,如果某一其他元素相关,那么根据对称和传递性,仍然自反),故可以求出m个元素的等价关系的数量,再加入n-m个空元素,,等价关系的数量即bell数,可由stirling数计算。

#include <bits/stdc++.h>
#define pb push_back
#define se second
#define fs first
#define sq(x) (x)*(x)
#define eps 0.000000001
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const ll mod=1e9+7;
const int maxv=4005;
ll S[maxv][maxv];
ll B[maxv];
ll C[maxv][maxv]; 
void init(){
    for(int i=1;i<maxv;i++){
        S[i][i]=1;
    }
    B[0]=B[1]=1;
    for(int i=2;i<maxv;i++){
        for(int j=1;j<maxv;j++){
            S[i][j]=(S[i-1][j-1]+S[i-1][j]*j%mod)%mod;
            B[i]=(B[i]+S[i][j])%mod;
        }
    }
    for(int i=0;i<maxv;i++){
        C[i][i]=1;
        C[i][0]=1;
    }
    for(int i=1;i<maxv;i++){
        for(int j=1;j<maxv;j++){
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
        }
    }
}
int main(){
//    freopen("/home/files/CppFiles/in","r",stdin);
    init();
    int n;
    while(cin>>n){
         ll ans=0;
         for(int i=0;i<n;i++){
             ans=(ans+B[i]*C[n][i])%mod;
         }
         cout<<ans<<endl;
    }
    return 0;
}
View Code

 18 HDU3068(裸manacher算法

题目:最长回文连续串。

思路:http://blog.csdn.net/ggggiqnypgjg/article/details/6645824/   裸算法,参考此文

#include <bits/stdc++.h>
#define pb push_back
#define se second
#define fs first
#define sq(x) (x)*(x)
#define eps 0.000000001
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int maxv=2e5;
char read[maxv];
char s[maxv*2];
int p[maxv];
int len;
int h;
void manacher(){
    p[1]=1;
    int mx=0;
    int mxid=1;
    for(int i=1;i<h-1;i++){
        if(mx>i){
            p[i]=min(p[mxid*2-i],mx-i);
        }else{
            p[i]=1;
        }
        for(;s[i+p[i]]==s[i-p[i]];p[i]++);
        if(p[i]+i>mx){
            mx=p[i]+i;
            mxid=i;
        }
    }
}
int main(){
    ///freopen("/home/files/CppFiles/in","r",stdin);
    while(scanf("%s",read)!=EOF){
        len=strlen(read);
        s[0]='$';
        h=1;
        for(int i=0;i<len;i++){
            s[h++]='#';
            s[h++]=read[i];
        }
        s[h++]='#';
        s[h++]='&';
        manacher();
        int ans=0;
        for(int i=1;i<h-1;i++){
            ans=max(ans,p[i]-1);
        }
        printf("%d\n",ans);
    }
    return 0;
}
View Code

 19 HDU 5371(回文串,set

题目:给出一个数列,要求求出满足类似12332123这种关系的子串的最大长度。

思路:先利用manacher算法预处理出以每个数字为中心的回文串的最大长度,然后按照回文长度排序,利用set更新ans。

参考多校题解:http://bestcoder.hdu.edu.cn/blog/2015-multi-university-training-contest-7-solutions-by-uestc/

#include <bits/stdc++.h>
#define pb push_back
#define se second
#define fs first
#define sq(x) (x)*(x)
#define eps 0.000000001
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int maxv=1e5+3000;
int a[maxv*2];
int p[maxv*2];
P o[maxv*2];
int T,n;
void manacher(){
    int mx=0;
    int mxid;
    int i;
    for(int i=1;i<2*n+2;i++){
        if(mx>i){
            p[i]=min(p[mxid*2-i],mx-i);
        }else{
            p[i]=1;
        }
        for(;a[i-p[i]]==a[i+p[i]];p[i]++);
        if(mx<p[i]+i){
            mx=p[i]+i;
            mxid=i;
        }
        if(i%2==1){
            o[i/2]=P(p[i],i/2);
        }
    }
}
set<int> S;
int cas=0;
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    cin>>T;
    while(T--){
        S.clear();
        cin>>n;
        a[0]=-1;
        for(int i=1;i<=n;i++){
            scanf("%d",a+i*2);
            a[2*i-1]=-2;
        }
        a[2*n+1]=-2;
        a[2*n+2]=-3;
        manacher();
        sort(o+1,o+n+1,greater<P>() );
        int ans=0;
        for(int i=1;i<=n;i++){
            int l=o[i].fs-1;
            int s=o[i].se;
            set<int>::iterator it=S.upper_bound(s+l/2);
            if(it!=S.begin()){
                it--;
                ans=max(ans,*it-s);
            }
            it=S.lower_bound(s-l/2);
            if(it!=S.end()) ans=max(ans,s-*it);
            S.insert(s);
        }
        printf("Case #%d: %d\n",++cas,ans*3);
    }
    return 0;
}
View Code

20 HDU 5372(树状数组

题目:每次添加或删除一条线段,询问某区间内的线段条数。

思路:这题有个关键条件是每次添加的线段长度+1,这样的话不会出现先添加的线段包括后面线段的情况,所以只需要两个树状数组维护起止端点即可。没想到简直是智商太低。。。。

#include <bits/stdc++.h>
#define pb push_back
#define se second
#define fs first
#define sq(x) (x)*(x)
#define eps 0.000000001
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int maxv=2e5+300;
struct BIT{
    ll a[maxv*3];
    void add(int p,int x){
        while(p<maxv*3){
            a[p]+=x;
            p+=p&-p;
        }
    }
    int sum(int p){
        int ans=0;
        while(p>0){
            ans+=a[p];
            p-=p&-p;
        }
        return ans;
    }
    void clear(){
        memset(a,0,sizeof a);
    }
}A,B;
int n;
int h,add,tol;
int opa[maxv];
P op[maxv];
ll disc[maxv*3];                  
int cas=0;
int idx(int x){
    return lower_bound(disc,disc+tol,x)-disc+1;
}
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    while(cin>>n){
        int add=1;
        A.clear(),B.clear();
        h=0;
        add=1;
        printf("Case #%d:\n",++cas);
        for(int i=0;i<n;i++){
            int o,b;
            scanf("%d%d",&o,&b);
            op[i].fs=o,op[i].se=b;
            if(o==0){
                disc[h++]=op[i].se;
                disc[h++]=op[i].se+add;
                add++;
            }
        }
        sort(disc,disc+h);
        add=1;
        tol=unique(disc,disc+h)-disc;
        for(int i=0;i<n;i++){
            if(op[i].fs==0){
                int x=idx(op[i].se),y=idx(op[i].se+add);
                printf("%d\n",-A.sum(x-1)+B.sum(y));
                opa[add]=op[i].se;
                A.add(x,1);
                B.add(y,1);
                add++;
            }else{
                int x=idx(opa[op[i].se]),y=idx(opa[op[i].se]+op[i].se);
                A.add(x,-1);
                B.add(y,-1);
            }
        }
    }
    return 0;
}
View Code

 21 HDU 5379(树形dp

题目:给一个树的每个节点赋值,使得每个节点的子节点标号连续,每个节点的所有后代节点的标号也连续,标号1~n,问有多少种赋值方法。

思路:预处理出每个节点的后代节点,叶子节点,非叶子节点个数,dp处理每棵子树的方案数。

参考多校题解:http://bestcoder.hdu.edu.cn/blog/2015-multi-university-training-contest-7-solutions-by-uestc/

#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
using namespace std;
#define IOS std::ios::sync_with_stdio(false);std::cin.tie(0)
#define pb push_back
#define PB pop_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.00000001
#define IINF (1<<30)
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> P;
const ll mod=1e9+7;
const int maxv=1e5+400;
int n;
vector<int> G[maxv];
int tsonnum[maxv];
int sonnum[maxv];
int dsonnum[maxv];
ll f[maxv];
int T;
void init(){
    f[0]=1;
    for(int i=1;i<maxv;i++){
        f[i]=(f[i-1]*i)%mod;
    }
}
void clear(){
    for(int i=0;i<maxv;i++){
        G[i].clear();
        tsonnum[i]=0;
        sonnum[i]=0;
        dsonnum[i]=0;
    }
    sonnum[maxv-1]=1;
}
void cont(int v,int fa){
    tsonnum[v]=1;
    for(int i=0;i<G[v].size();i++){
        int u=G[v][i];
        if(u==fa) continue;
        cont(u,v);
        sonnum[v]++;
        tsonnum[v]+=tsonnum[u];
        if(tsonnum[u]>1){
            dsonnum[v]++;
        }
    }
}
ll dfs(int v,int fa){
    if(dsonnum[v]>2){
        return 0;
    }
    ll ans=1;
    for(int i=0;i<G[v].size();i++){
        int u=G[v][i];
        if(u==fa) continue;
        ans=(ans*dfs(u,v))%mod;
    }
    ans=(ans*f[sonnum[v]-dsonnum[v]])%mod;
    if(dsonnum[v]<=1&&tsonnum[v]>1) ans=(ans*2)%mod;
    return ans;
}
int cas=0;
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    init();
    cin>>T;
    while(T--){
        cin>>n;
        clear();
        for(int i=0;i<n-1;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            G[x].pb(y);
            G[y].pb(x);
        }
        cont(1,-1);
        printf("Case #%d: %lld\n",++cas,dfs(1,-1));
    }
    return 0;
}
View Code

 22 CF Gym 100379H(博弈问题

题目:两个人轮流取石子,每次最多可取logn个,无法操作就输,问n个石子时第一个是必胜还是必败。

思路:石子数在2^k-1~2^(k+1)之间的必败态间隔为k,由此可以递推出必败态的位置。注意有个坑点是如果+k后正好是2的幂,那么这个不是必败态,因为此时可以加的数变成了k+1.递推必败态的方式很巧妙。

#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
#define pb push_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.0000000001
#define IINF (1<<30)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
ll n;
int T;
int main(){
//    freopen("/home/files/CppFiles/in","r",stdin);
    //freopen("/home/files/CppFiles/out","w",stdout);
    cin>>T;
    while(T--){
        cin>>n;
        if(n==1){
            puts("2");
            continue;
        }else if(n==2){
            puts("1 1");
            continue;
        }
        ll k=4,b=2,s=3;
        while((k<<1)<=n){
            k<<=1;
            s+=(k-s)/(b+1)*(b+1);
            if(s==k) s-=b+1;
            b++;
        }
        if((n-s)%(b+1)==0){
            printf("%lld\n",2LL);
        }else{
            ll add=(n-s)/(b+1);
            add=s+add*(b+1);
            printf("%lld %lld\n",1LL,n-add);
        }
    }
    return 0;
}
View Code

 23 CF Gym 100379G(博弈问题

题目:三堆石子,两个人轮流操作,可以拿走一堆里的任意数量或者三堆都拿走同样数量。问谁能赢。

思路:Nim游戏的变种,实际上仍然是简单的异或就能搞定,异或是0为必败态,理由如下:对于必胜态,一定可以取一堆石子中的若干使得nim值为0,对于必败态,不论如何取(如果三堆都取,那么取后nim值不为0,“如果为0,那么取之前的nim值就不能为0,因为是3堆,矛盾”,如果只取一堆,显然)。这样必胜和必败态的转移就与nim游戏一样了。

24 POJ 1274(二分匹配

裸题,好久没写了,贴个代码。

/*
*@author:  Cwind
*http://www.cnblogs.com/Cw-trip/
*/
#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
using namespace std;
#define IOS std::ios::sync_with_stdio(false);std::cin.tie(0)
#define pb push_back
#define PB pop_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.00000001
#define IINF (1<<29)
#define LINF (1ll<<59)
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> P;
const int maxn=205;
vector<int> G[maxn*4];
int n,m;
int match[maxn*4];
bool used[maxn*4];
inline void addedge(int a,int b){
    G[a].pb(b);
    G[b].pb(a);
}
bool dfs(int v){
    used[v]=1;
    for(int i=0;i<G[v].size();i++){
        int u=G[v][i],w=match[u];
        if(w<0||!used[w]&&dfs(w)){
            match[v]=u;
            match[u]=v;
            return 1;
        }
    }
    return 0;
}
inline int bi_match(){
    int res=0;
    memset(match,-1,sizeof match);
    for(int i=1;i<=200+n;i++){
        if(match[i]==-1){
            memset(used,0,sizeof used);
            if(dfs(i)){
                res++;
            }
        }
    }
    return res;
}
inline void clear(){
    for(int i=0;i<maxn*4;i++){
        G[i].clear();
    }
}
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    while(cin>>n>>m){
        clear();
        for(int i=1;i<=n;i++){
            int ss;
            scanf("%d",&ss);
            for(int j=0;j<ss;j++){
                int k;
                scanf("%d",&k);
                addedge(i,k+200);
            }
        }
        cout<<bi_match()<<endl;
    }
    return 0;
}
View Code

 25 HDU 1542(矩形面积并,线段树

裸题,用线段树维护当前扫描线扫过的矩形部分总长度。

/*
* @author:  Cwind
* http://www.cnblogs.com/Cw-trip/
*/
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
using namespace std;
#define IOS std::ios::sync_with_stdio(false);std::cin.tie(0)
#define pb push_back
#define PB pop_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.00000001
#define IINF (1<<29)
#define LINF (1ll<<59)
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> P;
const int maxn=105;
struct EDGE{
    double l,r,h;
    int u;
    EDGE(double ll,double rr,double hh,int uu):l(ll),r(rr),h(hh),u(uu){}
};
bool cmp(const EDGE &a,const EDGE &b){return a.h<b.h;}
int n;
double decx[maxn*2];
vector<EDGE> es;
inline void add(double x1,double y1,double x2,double y2){
    es.pb(EDGE(x1,x2,y2,-1));
    es.pb(EDGE(x1,x2,y1,1));
}
struct Node {
    int l,r;
    double vl,vr;
    double len,val;
    int num;
    Node *ch[2];
}pool[maxn*10];
int ph=0;
Node *newNode(){
    Node *n=&pool[ph++];
    n->val=n->num=0;
    return n;
}
Node *root;
void build(Node *n,int l,int r){
    n->l=l,n->r=r;
    if(r-l<=1){
        n->vr=decx[r];
        n->vl=decx[l];
        n->len=n->vr-n->vl;
        return;
    }
    n->ch[0]=newNode();
    n->ch[1]=newNode();
    build(n->ch[0],l,(l+r)/2);
    build(n->ch[1],(l+r)/2,r);
    n->len=n->ch[1]->vr-n->ch[0]->vl;
    n->vr=n->ch[1]->vr,n->vl=n->ch[0]->vl;
}
void insert(Node *n,int a,int b,int u){
    int l=n->l,r=n->r;
    if(l>=b||a>=r) return;
    Node *chl=n->ch[0],*chr=n->ch[1];
    if(a<=l&&b>=r){
        n->num+=u;
        if(n->num==0){
            if(r-l>1)
                n->val=chl->val+chr->val;
            else
                n->val=0;
        }
        if(n->num==1&&u==1){
            n->val=n->len;
        }
        return;
    }
    insert(chl,a,b,u);
    insert(chr,a,b,u);
    if(!n->num) n->val=chl->val+chr->val;
}
inline double query(){
    return root->val;
}
inline void init(){
    ph=0;
    root=newNode();
    es.clear();
}
int xn;
#define idx(x) (lower_bound(decx,decx+xn,(x))-decx)
int cas=0;
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    while(cin>>n,n){
        init();
        double x1,y1,x2,y2;
        for(int i=0;i<n;i++){
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            add(x1,y1,x2,y2);
            decx[2*i]=x1,decx[2*i+1]=x2;
        }
        sort(es.begin(),es.end(),cmp);
        sort(decx,decx+2*n);
        int xn=unique(decx,decx+2*n)-decx;
        build(root,0,xn-1);
        double fore=0,forh=0;
        double ans=0;
        for(int i=0;i<es.size();i++){
            EDGE &e=es[i];
            ans+=(fore)*(e.h-forh);
            insert(root,idx(e.l),idx(e.r),e.u);
            forh=e.h;
            fore=query();
        }
        printf("Test case #%d\n",++cas);
        printf("Total explored area: %.2f\n\n",ans);
    }
    return 0;
}
View Code

 26 PKU1442 (查询区间第k大,treap裸题

大白书模板

/*
* @author:  Cwind
* http://www.cnblogs.com/Cw-trip/
*/
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
using namespace std;
#define IOS std::ios::sync_with_stdio(false);std::cin.tie(0)
#define pb push_back
#define PB pop_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.00000001
#define IINF (1<<29)
#define LINF (1ll<<59)
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> P;
const int maxn=30030;
struct Node{
    Node *ch[2];
    int right,val;
    int sz;
    Node():sz(0){}
    int cmp(int x) const {
        if(x==val) return -1;
        return x>val;
    }
    void maintain(){
        sz=ch[0]->sz+ch[1]->sz+1;
    }
}pool[maxn*10];
int ph=0;
Node *null=new Node();
Node *newNode(int v){
    Node *n=&pool[ph++];
    n->val=v;
    n->right=rand();
    n->ch[0]=n->ch[1]=null;
    return n;
}
void rotate(Node *&o,int d){
    Node *k=o->ch[d^1];
    o->ch[d^1]=k->ch[d];
    k->ch[d]=o;
    o->maintain();
    k->maintain();
    o=k;
}
void insert(Node *&o,int x){
    if(o==null) o=newNode(x);
    else{
        int d=o->val<x;
        insert(o->ch[d],x);
        if(o->ch[d]->right>o->right) rotate(o,d^1);
    }
    o->maintain();
}
void remove(Node *&o,int x){
    int d=o->cmp(x);
    if(d==-1){
        if(o->ch[0]==null) o=o->ch[1];
        else if(o->ch[1]==null) o=o->ch[0];
        else{
            int d2=o->ch[0]->right>o->ch[1]->right;
            rotate(o,d2);
            remove(o->ch[d2],x);
        }
    }else remove(o->ch[d],x);
    o->maintain();
}
int find(Node *o,int x){
    while(o!=null){
        int d=o->cmp(x);
        if(d==-1) return 1;
        else o=o->ch[d];
    }
    return 0;
}
int kth(Node *o,int k){
    if(k==o->ch[0]->sz+1) return o->val;
    if(o->ch[0]->sz>=k){
        return kth(o->ch[0],k);
    }
    else{
        return kth(o->ch[1],k-o->ch[0]->sz-1);
    }
}
int m,n;
int a[maxn],u[maxn];
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    cin>>m>>n;
    for(int i=1;i<=m;i++){
        scanf("%d",a+i);
    }
    for(int i=1;i<=n;i++){
        scanf("%d",u+i);
    }
    int qh=1;
    Node *root=null;
    for(int i=1;i<=m;i++){
        insert(root,a[i]);
        while(qh<=n&&u[qh]==i){
            printf("%d\n",kth(root,qh));
            qh++;
        }
    }
    return 0;
}
View Code

 27 POJ 2175 (最小费用流,消负圈算法

题目:有n个居民楼和m个防空洞,每个防空洞有一个人口上限,总花费为每个人到各自防空洞的曼哈顿距离之和,现在给出一个避难方案,问是否是使总花费最小的方案,若不是,输出一组更小的方案。

思路:对于已经有的方案,建立残余网络,若网络中有负环,则沿该环增广(流量增加1即可),即可找到一组更优的解。

实现:可以使用矩阵保存图,使用bellmanford或floyd算法找到负环,更最短路时要记录前驱节点,找到负环后沿着前驱路径增广。如果某居民楼的有人到某防空洞避难,那么从防空洞到居民楼连边(费用为-c),但是不论人数为多少,居民楼到防空洞均连边(因为增广时必然要通过某边将流量退回),此题不必设置源点(必须满流),但是汇点是必要的,对于到汇点有路径的边,其费用为0.

/*
*@author:  Cwind
*http://www.cnblogs.com/Cw-trip/
*/
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
#define pb push_back
#define PB pop_back
#define bk back()
#define fs first
#define se second
#define sq(x) (x)*(x)
const int IINF =(1<<29);
const double eps=1e-10;
using namespace std;
typedef long long ll;
//typedef pair<ll,ll> P;
const int maxn=300;
int N,M;
int E[maxn][maxn];
int dis[maxn][maxn];
int G[maxn][maxn];
int X[maxn],Y[maxn],B[maxn];
int P[maxn],Q[maxn],C[maxn];
int prev[maxn][maxn];
bool used[maxn];
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    scanf("%d%d",&N,&M);
    for(int i=1;i<=N;i++){
        scanf("%d%d%d",&X[i],&Y[i],&B[i]);
    }
    for(int i=1;i<=M;i++){
        scanf("%d%d%d",&P[i],&Q[i],&C[i]);
    }
    for(int i=1;i<=N;i++){
        for(int j=1;j<=M;j++){
            scanf("%d",&E[i][j]);
        }
    }
    for(int i=0;i<maxn;i++){
        for(int j=0;j<maxn;j++){
            G[i][j]=IINF;
        }
    }
    for(int j=1;j<=M;j++){
        int sum=0;
        for(int i=1;i<=N;i++){
            int dd=abs(X[i]-P[j])+abs(Y[i]-Q[j])+1;
            sum+=E[i][j];
            G[i][j+N]=dd;
            if(E[i][j]) G[j+N][i]=-dd;
        }
        if(sum>0){
            G[M+N+1][j+N]=0;
        }
        if(sum<C[j]){
            G[j+N][M+N+1]=0;
        }
    }
    for(int i=1;i<=N+M+1;i++){
        for(int j=1;j<=N+M+1;j++){
            prev[i][j]=i;
        }
    }
    for(int k=1;k<=M+N+1;k++){
        for(int i=1;i<=M+N+1;i++){
            for(int j=1;j<=N+M+1;j++){
                if(G[i][j]>G[i][k]+G[k][j]){
                    G[i][j]=G[i][k]+G[k][j];
                    prev[i][j]=prev[k][j];
                    if(i==j&&G[i][j]<0){
                        memset(used,0,sizeof used);
                        for(int v=i;!used[v];v=prev[i][v]){
                            used[v]=1;
                            if(v!=N+M+1&&prev[i][v]!=N+M+1){
                                if(v>N){
                                    E[prev[i][v]][v-N]++;
                                }else{
                                    E[v][prev[i][v]-N]--;
                                }
                            }
                        }
                        puts("SUBOPTIMAL");
                        for(int x=1;x<=N;x++){
                            for(int y=1;y<=M;y++){
                                printf("%d%c",E[x][y],y==M?'\n':' ');
                            }
                        }
                        return 0;
                    }
                }
            }
        }
    }
    puts("OPTIMAL");
    return 0;
}
View Code

 28 URAL 1774(最大流

题目:烙煎饼,每个煎饼要烙两面,但是每个煎饼有个特定的时间段内才能烙,而锅里每次只能同时烙最多k张煎饼,问是否能烙完所有煎饼。

思路:源点到每张煎饼连一条容量为2的边,每个煎饼向可行的时间连1的边,每个时间向汇点连容量k的边,求最大流即可。

/*
* @author:  Cwind
* http://www.cnblogs.com/Cw-trip/
*/
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#define pb push_back
#define fs first
#define se second
#define bk back()
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;


///
const int IINF=(1<<29);
const int MAXV=100000;
struct EDGE{
    int to,cap,rev;
    EDGE(int t,int c,int r):to(t),cap(c),rev(r){}
};
vector<EDGE> G[MAXV];    
void addedge(int from,int to,int cap){///加边
    G[from].pb(EDGE(to,cap,G[to].size()));
    G[to].pb(EDGE(from,0,G[from].size()-1));
}
int level[MAXV];
queue<int> Q;
void bfs(int s){////bfs出分层图
    memset(level,-1,sizeof level);
    level[s]=0;
    Q.push(s);
    while(!Q.empty()){
        int v=Q.front();Q.pop();
        for(int i=0;i<G[v].size();i++){
            EDGE &e=G[v][i];
            if(e.cap>0&&level[e.to]==-1){
                level[e.to]=level[v]+1;
                Q.push(e.to);
            }
        }
    }
}
int iter[MAXV];
int dfs(int v,int t,int f){///dfs寻找增广路径
    if(v==t) return f;
    for(int &i=iter[v];i<G[v].size();i++){
        EDGE &e=G[v][i];
        if(e.cap>0&&level[e.to]>level[v]){
            int d=dfs(e.to,t,min(f,e.cap));
            if(d>0){
                e.cap-=d;
                G[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}
int dinic(int s,int t){///dinic算法求解最大流
    int flow=0;
    for(;;){
        bfs(s);
        if(level[t]==-1) return flow;
        memset(iter,0,sizeof iter);
        int f;
        while((f=dfs(s,t,IINF))>0) flow+=f;
    }
    return 0;
}
int s=MAXV-1;
int t=MAXV-2;

/
const int maxn=10000;
int n,k;
int ss[maxn],tt[maxn];
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>tt[i]>>ss[i];
        for(int j=tt[i]+1;j<=tt[i]+ss[i];j++){
            addedge(i,j+n,1);
        }
    }
    for(int i=1;i<=n;i++){
        addedge(s,i,2);
    }
    for(int i=n+1;i<=n+2000;i++){
        addedge(i,t,k);
    }
    int flow=dinic(s,t);
    if(flow==n*2){
        puts("Yes");
        for(int i=1;i<=n;i++){
            for(int j=0;j<G[i].size();j++){
                if(G[i][j].cap==0){
                    printf("%d ",G[i][j].to-n-1);
                }
            }
            puts("");
        }
    }else{
        puts("No");
    }
    return 0;
}
View Code

 29 Gym 100338C (最短路,求割边。

题目:求删掉一条路边使得最短路变长的边,有重边。

思路:本来思路是很简单的,求出最短路上的边建图,然后求桥就好。但是题目强调了有重边,结果就想着要处理重边,然后wa了一整天。其实这题重边完全不用管,(当然处理掉也可以,不知为啥我一直wa。。)。tarjan算法求桥的时候,dfs只要判断一下不要经过之前经过的边就好了,正好也方便了输出边的编号。

/*
* @author:  Cwind
* http://www.cnblogs.com/Cw-trip/
*/
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#define pb push_back
#define fs first
#define se second
#define bk back()
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;

const int maxn=20030;
const int maxm=1e5+300;
struct EDGE{
    int to,d,next,id;
}es1[maxm*2];
int eh1=0;
int head1[maxn];
void addedge1(int from,int to,int d,int id){
    es1[eh1].to=to,es1[eh1].d=d;es1[eh1].id=id;
    es1[eh1].next=head1[from];
    head1[from]=eh1++;
}
ll dis1[maxn],dis2[maxn];
void dij(int s,ll *dis){
    priority_queue<P,vector<P>, greater<P> > Q;
    memset(dis,0x2f,sizeof dis1);
    dis[s]=0;
    Q.push(P(0,s));
    while(!Q.empty()){
        P now=Q.top();Q.pop();
        if(dis[now.se]<now.fs) continue;
        for(int i=head1[now.se];i!=-1;i=es1[i].next){
            EDGE &e=es1[i];
            if(dis[e.to]>dis[now.se]+e.d){
                dis[e.to]=dis[now.se]+e.d;
                Q.push(P(dis[e.to],e.to));
            }
        }
    }

}
struct any{
    int len,num,id;
    any(int len,int num,int id):len(len),num(num),id(id){}
    any(){}
};
map<P,any> M;
map<P,any> ::iterator it1,it2;
int n,m;
vector<P> G[maxn];
int od[maxn];
int low[maxn];
int clo=0;
vector<P> ce;//割边
void addedge2(int from,int to,int id){
    G[from].pb(P(to,id));
    G[to].pb(P(from,id));
}
ll mindis;
void rebuild(){
    for(int i=1;i<=n;i++){
        for(int j=head1[i];j!=-1;j=es1[j].next){
            EDGE &e=es1[j];
            if(dis1[i]+dis2[e.to]+e.d==mindis){
                addedge2(i,e.to,e.id);
                addedge2(e.to,i,e.id);
            }
        }
    }
}
vector<int> ans;
void dfs(int v,int f){
    low[v]=od[v]=++clo;
    for(int i=0;i<G[v].size();i++){
        int u=G[v][i].fs;
        if(G[v][i].se==f) continue;
        if(!od[u]){
            dfs(u,G[v][i].se);
            low[v]=min(low[v],low[u]);
            if(low[u]>od[v]){
                ans.pb(G[v][i].se);
            }
        }
        else
            low[v]=min(low[v],od[u]);
    }
}

map<P,int > id;
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    //freopen("important.in","r",stdin);
    //freopen("important.out ","w",stdout);
    memset(head1,-1,sizeof head1);
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        addedge1(a,b,c,i);
        addedge1(b,a,c,i);
    }
    dij(1,dis1);
    dij(n,dis2);
    mindis=dis1[n];
    rebuild();
    dfs(1,-1);
    for(int i=0;i<ce.size();i++){
        ans.pb(id[ce[i]]);
    }
    sort(ans.begin(),ans.end());
    ans.erase(unique(ans.begin(),ans.end()),ans.end());
    cout<<ans.size()<<endl;
    for(int i=0;i<ans.size();i++){
        printf("%d ",ans[i]);
    }
    return 0;
}
View Code

 30 POJ 3463(求最短路和次短路的条数

思路:dijkstra的基本思想,每次取出最小的未扩展的节点扩展,此时此节点的最短路不可能再增长(也不可能有长度一样的其他新路径,因为边长为正),所以标记此节点并扩展,由于要求最短路和次短路,实际上要做2×n次扩展。选择最小的点时可以用优先队列,也可以直接遍历,因为此题数据比较小。

注意一般的dijkstra算法是不用标记vis的,因为不会出现重复入队的情况,但是此题不标记的话区分不了最短路和次短路,会爆内存。

/*
* @author:  Cwind
* http://www.cnblogs.com/Cw-trip/
*/
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
using namespace std;
#define IOS std::ios::sync_with_stdio(false);std::cin.tie(0)
#define pb push_back
#define PB pop_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.00000001
#define IINF (1<<29)
#define LINF (1ll<<59)
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> P;
struct EDGE{
    int to,d;
    EDGE(int to,int d):to(to),d(d){}
};
const int maxn=1030;
vector<EDGE> G[maxn];
int N,M;
int S,F;
int dis[maxn][2];
int cnt[maxn][2];
bool vis[maxn][2];
const int inf=1e9+300000;
void work(){
    memset(dis,0x3f,sizeof dis);
    memset(cnt,0,sizeof cnt);
    memset(vis,0,sizeof vis);
    dis[S][0]=0;
    cnt[S][0]=1;
    for(;;){
        bool find=0;
        bool witch;
        int v;
        int mindis=inf;
        for(int i=1;i<=N;i++){
            if(!vis[i][0]&&dis[i][0]<mindis){
                find=1;
                mindis=dis[i][0];
                witch=0;
                v=i;
            }else if(!vis[i][1]&&dis[i][1]<mindis){
                find=1;
                mindis=dis[i][1];
                witch=1;
                v=i;
            }
        }
        if(!find) break;
        vis[v][witch]=1;
        for(int i=0;i<G[v].size();i++){
            EDGE &e=G[v][i];
            if(dis[e.to][0]>dis[v][witch]+e.d){
                swap(dis[e.to][0],dis[e.to][1]);
                swap(cnt[e.to][0],cnt[e.to][1]);
                dis[e.to][0]=dis[v][witch]+e.d;
                cnt[e.to][0]=cnt[v][witch];
            }else if(dis[e.to][0]==dis[v][witch]+e.d){
                cnt[e.to][0]+=cnt[v][witch];
            }else if(dis[e.to][1]>dis[v][witch]+e.d){
                dis[e.to][1]=dis[v][witch]+e.d;
                cnt[e.to][1]=cnt[v][witch];
            }else if(dis[e.to][1]==dis[v][witch]+e.d){
                cnt[e.to][1]+=cnt[v][witch];
            }
        }
    }
}
int T;
void clear(){
    for(int i=0;i<maxn;i++){
        G[i].clear();
    }
}
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    cin>>T;
    while(T--){
        clear();
        cin>>N>>M;
        for(int i=0;i<M;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            G[a].pb(EDGE(b,c));
        }
        cin>>S>>F;
        work();
        if(dis[F][1]-1==dis[F][0]){
            cout<<cnt[F][1]+cnt[F][0]<<endl;
        }else{
            cout<<cnt[F][0]<<endl;
        }
    }
    return 0;
}
View Code

 优先队列版:

/*
* @author:  Cwind
* http://www.cnblogs.com/Cw-trip/
*/
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
using namespace std;
#define IOS std::ios::sync_with_stdio(false);std::cin.tie(0)
#define pb push_back
#define PB pop_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.00000001
#define IINF (1<<29)
#define LINF (1ll<<59)
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> P;
struct EDGE{
    int to,d;
    EDGE(int to,int d):to(to),d(d){}
};
const int maxn=1030;
vector<EDGE> G[maxn];
struct any{
    int v,d,witch;
    any(int v,int d,int witch):v(v),d(d),witch(witch){}
    bool operator < (const any &C)const {
        return d>C.d;
    }
};
int N,M;
int S,F;
int dis[maxn][2];
int cnt[maxn][2];
bool vis[maxn][2];
const int inf=1e9+300000;
void work(){
    memset(dis,0x3f,sizeof dis);
    memset(cnt,0,sizeof cnt);
    memset(vis,0,sizeof vis);
    dis[S][0]=0;
    cnt[S][0]=1;
    priority_queue<any> Q;
    Q.push(any(S,0,0));
    while(!Q.empty()){
        bool witch=Q.top().witch;
        int v=Q.top().v;
        int mindis=Q.top().d;
        Q.pop();
        if(vis[v][witch]||dis[v][witch]<mindis) continue;
        vis[v][witch]=1;
        for(int i=0;i<G[v].size();i++){
            EDGE &e=G[v][i];
            if(dis[e.to][0]>dis[v][witch]+e.d){
                swap(dis[e.to][0],dis[e.to][1]);
                swap(cnt[e.to][0],cnt[e.to][1]);
                dis[e.to][0]=dis[v][witch]+e.d;
                cnt[e.to][0]=cnt[v][witch];
                Q.push(any(e.to,dis[e.to][0],0));
                Q.push(any(e.to,dis[e.to][1],1));
            }else if(dis[e.to][0]==dis[v][witch]+e.d){
                cnt[e.to][0]+=cnt[v][witch];
            }else if(dis[e.to][1]>dis[v][witch]+e.d){
                dis[e.to][1]=dis[v][witch]+e.d;
                cnt[e.to][1]=cnt[v][witch];
                Q.push(any(e.to,dis[e.to][1],1));
            }else if(dis[e.to][1]==dis[v][witch]+e.d){
                cnt[e.to][1]+=cnt[v][witch];
            }
        }
    }
}
int T;
void clear(){
    for(int i=0;i<maxn;i++){
        G[i].clear();
    }
}
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    cin>>T;
    while(T--){
        clear();
        cin>>N>>M;
        for(int i=0;i<M;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            G[a].pb(EDGE(b,c));
        }
        cin>>S>>F;
        work();
        if(dis[F][1]-1==dis[F][0]){
            cout<<cnt[F][1]+cnt[F][0]<<endl;
        }else{
            cout<<cnt[F][0]<<endl;
        }
    }
    return 0;
}
View Code

需要注意的一点是:不论是最短路还是次短路,当某点的路径长度更新时就要入队。一开始写没有在第一种情况(新路短于最短路)把次短路也入队,结果wa了。(更新路径长度就要入队!!)

31 CodeForces571A(数学题

题目:给三个数abc,还有l,可以给abc增加总共不超过l的长度,问可以组成三角形的不同组合有多少种。

思路:比较考验数学思维的一道题,比赛的的时候一直想不清楚(虽然枚举最长边然后减去不可行的方案是很容易想到的)。实际上对于确定的最长边,不可行的组合就是所有使得b+c的长度小于等于a的 长度的组合(并且这种算法不会重复)。

/*
 * @author:  Cwind
 * http://www.cnblogs.com/Cw-trip/
 */
import java.util.*;
import java.util.concurrent.PriorityBlockingQueue;
import java.awt.print.Printable;
import java.io.*;
import java.math.*;

public class Main {

    public static void main(String[] args) throws Exception {
        Scanner in = new Scanner(new File(
                "/home/develop/eclipse_file/ACMproject/src/in"));
        // PrintWriter out=new PrintWriter(new File(
        // "/home/develop/eclipse_file/ACMproject/src/out"));
        PrintWriter out = new PrintWriter(new BufferedOutputStream(System.out));
        // Scanner in=new Scanner(new BufferedInputStream(System.in));
        long a, b, c, l;
        a = in.nextLong();
        b = in.nextLong();
        c = in.nextLong();
        l = in.nextLong();
        long ans = (l + 1) * (l + 2) * (l + 3) / 6;
        for (int la = 0; la <= l; la++) {
            long up = Math.min(l - la, a - c - b + la);
            if (up >= 0)
                ans -= (up + 1) * (up + 2) / 2;
        }
        for (int lb = 0; lb <= l; lb++) {
            long up = Math.min(l - lb, b - c - a + lb);
            if (up >= 0)
                ans -= (up + 1) * (up + 2) / 2;
        }
        for (int lc = 0; lc <= l; lc++) {
            long up = Math.min(l - lc, c - b - a + lc);
            if (up >= 0)
                ans -= (up + 1) * (up + 2) / 2;
        }
        out.println(ans);
        in.close();
        out.close();
    }
}
View Code

 32 Gym 100342H(思维题

题目:给出点数n和边数m,要求构造出一个图,使得dijkstra算法中每个点的最短路变化次数之和最大。

思路:队友的构造感觉是非常优美啊。。。。首先,我们不妨设最后的距离是按照点标号升序的,那么dijkstra的改变次数有一个显然的上界,那就是前面的每个点都在该点扩展一次。现在我们要达到这个上界。那么我们先从1开始,考虑到顺序关系,可以向点i连长度为i-1的边,此时所有各点的距离正好是升序,这样下次要扩展的节点就是2,而扩展该节点要将其他后面的所有点的距离都减小,那么我们把2向所有后面的点连比当前距离恰小1的边(注意2的距离是0,而依次方法构造的所有点到1的最短路都是0,但是都要分i次减小)那么实际上我们从i到j的连的边长就是i-j-1(所有点最短路都是0)。

转载于:https://www.cnblogs.com/Cw-trip/p/4694789.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值