那些年没有AC的水题...

1 HDU 5492(dp

题目:一个数字矩阵,要求找到一条路径使得经过的数字的方差最小.

思路:稍微把式子变换一下,状态是走到当前格子和为k时最小平方和.

/*
* @author:  Cwind
*/
#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 bk back()
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps (1e-6)
#define IINF (1<<29)
#define LINF (1ll<<59)
#define INF (1000000000)
#define FINF (1e3)
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> P;

const int maxsum=1900;
const int maxn=40;
int T,n,m;
int grid[maxn][maxn];
int dp[maxn][maxn][maxsum];
int h[maxn][maxn];
int pos[maxn][maxn][maxsum];
int inf;
int cas=0;
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    //freopen("test.in","r",stdin);
    //freopen("test.out","w",stdout);
    memset(dp,0x3f,sizeof dp);
    inf=dp[0][0][0];
    cin>>T;
    while(T--){
        cin>>n>>m;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                scanf("%d",&grid[i][j]);
            }
        }
        dp[0][0][grid[0][0]]=sq(grid[0][0]);
        pos[0][0][0]=grid[0][0];
        h[0][0]=1;
        int ans=inf;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                for(int k=0;k<h[i][j];k++){
                    int v=pos[i][j][k];
                    if(dp[i+1][j][v+grid[i+1][j]]==inf){
                        pos[i+1][j][h[i+1][j]++]=v+grid[i+1][j];
                        dp[i+1][j][v+grid[i+1][j]]=dp[i][j][v]+sq(grid[i+1][j]);
                    }else{
                        dp[i+1][j][v+grid[i+1][j]]=
                            min(dp[i][j][v]+sq(grid[i+1][j]),
                                dp[i+1][j][v+grid[i+1][j]]);
                    }
                    if(dp[i][j+1][v+grid[i][j+1]]==inf){
                        pos[i][j+1][h[i][j+1]++]=v+grid[i][j+1];
                        dp[i][j+1][v+grid[i][j+1]]=dp[i][j][v]+sq(grid[i][j+1]);
                    }else{
                        dp[i][j+1][v+grid[i][j+1]]=
                            min(dp[i][j][v]+sq(grid[i][j+1]),
                                dp[i][j+1][v+grid[i][j+1]]);
                    }
                    if(i==n-1&&j==m-1){
                        ans=min(ans,dp[i][j][v]*(n+m-1)-v*v);
                    }
                    dp[i][j][v]=inf;
                }
                h[i][j]=0;
            }
        }
        for(int i=0;i<=max(n,m);i++){
            for(int j=0;j<h[i][m];j++){
                int v=pos[i][m][j];
                dp[i][m][v]=inf;
            }
            h[i][m]=0;
            for(int j=0;j<h[n][i];j++){
                int v=pos[n][i][j];
                dp[n][i][v]=inf;
            }
            h[n][i]=0;
        }
        printf("Case #%d: %d\n",++cas,ans);
    }
    return 0;
}
View Code

 2 CF 559C

题目:一个棋盘上有若干个格子不能经过,求从左上角走到右下角的方案数.

思路:dp[i]表示走到第i个不能走的点的走法种数,那么这个答案就等于总的走法减去禁止的走法,禁止的走法可以枚举经过的第一个禁止点的位置得到.

/*
* @author:  Cwind
*/
//#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 bk back()
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps (1e-6)
#define IINF (1<<29)
#define LINF (1ll<<59)
#define INF (1000000000)
#define FINF (1e3)
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> P;


const ll mod=1e9+7;
const int maxv=2e5+600;
ll inv[maxv];
ll fac[maxv];
ll qpow(ll a,ll p,ll m){
    ll ans=1;
    while(p){
        if(p&1) ans=(ans*a)%m;
        p>>=1;
        a=(a*a)%m;
    }
    return ans;
}
void init(){
    fac[0]=1;
    for(ll i=1;i<maxv;i++){
        fac[i]=fac[i-1]*i%mod;
    }
    inv[0]=1;
    for(ll i=1;i<maxv;i++){
        inv[i]=inv[i-1]*qpow(i,mod-2,mod)%mod;
    }
}
ll getC(int n,int r){
    return fac[n]*inv[n-r]%mod*inv[r]%mod;
}
const int maxn=2005;
struct Point{
    int x,y;
    bool operator < (const Point &C)const {
        if(x!=C.x) return x<C.x;
        else return y<C.y;
    }
}a[maxn];
ll h,w,n;
ll dp[maxn];
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    init();
    cin>>h>>w>>n;
    for(int i=1;i<=n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        a[i].x=x,a[i].y=y;
    }
    a[n+1]=(Point){h,w};
    sort(a+1,a+n+1);
    a[0]=(Point){1,1};
    for(int i=1;i<=n+1;i++){
        ll tmp=0;
        dp[i]=getC(a[i].x-a[0].x+a[i].y-a[0].y,a[i].x-a[0].x);
        for(int j=0;j<i;j++){
            if(a[j].y<=a[i].y)
                tmp=(tmp+dp[j]*getC(a[i].x-a[j].x+a[i].y-a[j].y,a[i].x-a[j].x)%mod)%mod;
        }
        tmp=mod-tmp;
        dp[i]=(dp[i]+tmp)%mod;
    }
    cout<<dp[n+1]<<endl;
    return 0;
}
View Code

 3 HDU 5469

题目:一棵树的每个节点有一个字母,问是否有一条路径组成了指定的字符串.

思路:直接hash+分治,写了差不多一整天....写到都感觉"啊..分治就这么点东西怎么就是不对呢...",然后看了会动漫调整了一下心态,出了个出错的数据才把这题过掉.....这种时候要出一些看起来不会错但是比较复杂的数据,一定不能偷懒...

/*
* @author:  Cwind
*/
//#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 bk back()
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps (1e-6)
#define IINF (1<<29)
#define LINF (1ll<<59)
#define INF (1000000000)
#define FINF (1e3)
#define mem(x) memset((x),0,sizeof (x));
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> P;

const ll mod=1e9+7;
const ll H=3457;
const int maxn=1e4+3000;
int T;
int n;
char c[maxn],s[maxn];
vector<int> G[maxn];
ll hp[maxn];
bool used[maxn];
int cnt;
int ch=0;
void count(int v,int f=-1){
    cnt++;
    for(int i=0;i<G[v].size();i++){
        int u=G[v][i];
        if(u==f||used[u]) continue;
        count(u,v);
    }
}
int snum[maxn];
int center,mind;
void getCenter(int v,int f=-1){
    snum[v]=0;
    int maxs=0;
    for(int i=0;i<G[v].size();i++){
        int u=G[v][i];
        if(u==f||used[u]) continue;
        getCenter(u,v);
        snum[v]+=snum[u]+1;
        maxs=max(maxs,snum[u]+1);
    }
    if(max(maxs,cnt-snum[v]-1)<mind){
        mind=max(maxs,cnt-snum[v]-1);
        center=v;
    }
}

int ls;
ll shash1[maxn],shash2[maxn];
int ff[maxn],aa[maxn];
int getfore(int len){return ((shash2[1]-(ll)shash2[len+1]*hp[len])%mod+mod+mod)%mod;}
int getafter(int len){return ((shash1[ls]-(ll)shash1[ls-len]*hp[len])%mod+mod+mod)%mod;}
int findfore[maxn],findafter[maxn];
int branch;

void calhash(int v,ll hash,int d,int f){
    ll val=(hash*H+c[v-1])%mod;
    if(val==ff[d+1]){
        if(!findfore[d+1]) findfore[d+1]=branch;
        else if(findfore[d+1]!=branch) findfore[d+1]=-1;
    }
    if(val==aa[d+1]){
        if(!findafter[d+1])findafter[d+1]=branch;
        else if(findafter[d+1]!=branch)findafter[d+1]=-1;
    }
    for(int i=0;i<G[v].size();i++){
        int u=G[v][i];
        if(u==f||used[u]) continue;
        calhash(u,val,d+1,v);
    }
}
bool solve(int v){
    used[v]=1;
    branch=1;
    ch=0;
    mem(findfore);mem(findafter);
    findfore[0]=findafter[0]=-1;
    for(int i=0;i<G[v].size();i++){
        int u=G[v][i];
        if(used[u]) continue;
        calhash(u,0,0,v);
        branch++;
    }
    for(int i=0;i<ls;i++){
        if(c[v-1]==s[i]){
            if(!findfore[i]||!findafter[ls-i-1])continue;
            if(findfore[i]>0&&findafter[ls-i-1]>0&&findfore[i]==findafter[ls-i-1])continue;
            return 1;
        }
    }
    for(int i=0;i<G[v].size();i++){
        int u=G[v][i];
        if(used[u]) continue;
        cnt=0;
        count(u);
        if(cnt<ls) continue;
        mind=1e9;
        getCenter(u);
        if(solve(center))return 1;
    }
    return 0;
}
int cas=0;
void init(){
    for(int i=0;i<=n;i++){
        G[i].clear();
        used[i]=0;
    }
}
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    hp[0]=1;
    for(int i=1;i<maxn;i++)
        hp[i]=hp[i-1]*H%mod;
    cin>>T;
    while(T--){
        cin>>n;
        init();
        for(int i=0;i<n-1;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            G[a].pb(b);
            G[b].pb(a);
        }
        scanf("%s%s",c,s);
        ls=strlen(s);
        for(int i=1;i<=ls;i++) shash1[i]=(shash1[i-1]*H%mod+s[i-1])%mod;
        shash2[ls+1]=0;
        for(int i=ls;i>=1;i--){shash2[i]=(shash2[i+1]*H%mod+s[i-1])%mod;}
        for(int i=0;i<=ls;i++) ff[i]=getfore(i),aa[i]=getafter(i);
        cnt=0;
        mind=1e9;
        count(1);
        getCenter(1);
        if(solve(center))printf("Case #%d: Find\n",++cas);
        else printf("Case #%d: Impossible\n",++cas);
    }
    return 0;
}
View Code

 4 HDU 5468

题目:给出一棵树,求每个节点的子树中和这个节点的值互质的数有多少个.

思路:由于只有1e5的数据,所以可以记录每个素数分解的倍数的个数,然后每个子树处理完之后和之前的值减一下就是当前子树中的值.然后容斥原理搞一搞就好了.

/*
* @author:  Cwind
*/
//#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 bk back()
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps (1e-6)
#define IINF (1<<29)
#define LINF (1ll<<59)
#define INF (1000000000)
#define FINF (1e3)
#define clr(x) memset((x),0,sizeof (x));
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> P;

const int maxn=1e5+400;
int n;
int cnt[maxn],val[maxn],ans[maxn];
vector<int> G[maxn],fac[maxn];
int mp[maxn];
void dfs(int v,int f=-1){
    int pre[80];
    clr(pre);
    int sz=fac[val[v]].size();
    for(int mask=0;mask<(1<<sz);mask++){
        int tmp=1;
        for(int j=0;j<sz;j++) if((1<<j)&mask) tmp*=fac[val[v]][j];
        pre[mask]=cnt[tmp];
        cnt[tmp]++;
    }
    for(int i=0;i<G[v].size();i++){
        int u=G[v][i];
        if(u==f) continue;
        dfs(u,v);
    }
    for(int mask=0;mask<(1<<sz);mask++){
        int tmp=1,f=-1;
        for(int j=0;j<sz;j++)if((1<<j)&mask)
            {tmp*=fac[val[v]][j];f*=-1;}
        ans[v]-=f*(cnt[tmp]-pre[mask]);
    }
}
void init_cal(){
    for(int i=2;i<maxn;i++)if(!mp[i])
        for(int j=i;j<maxn;j+=i){fac[j].pb(i);mp[j]=1;}
}
void init(){for(int i=0;i<=n;i++) G[i].clear();clr(cnt);clr(ans);}
int cas=0;
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    init_cal();
    while(cin>>n){
        init();
        for(int i=0;i<n-1;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            G[a].pb(b);
            G[b].pb(a);
        }
        for(int i=1;i<=n;i++) scanf("%d",&val[i]);
        dfs(1);
        printf("Case #%d:",++cas);
        for(int i=1;i<=n;i++) printf(" %d",ans[i]);
        puts("");
    }    
    return 0;
}
View Code

5  CF 582B

题目:一个数列复制t份首尾相接,求这个数列的最长递增子序列的长度.

思路:这题的一种做法是求前后n段的结果,中间的值都取相同的(只算一段直觉上也是对的,但是显然n段不可能有任何问题,所以不要作死)..另一种做法是矩阵快速幂..但是矩阵快速幂本质上都是dp,所以从dp的角度来考虑.状态dp[i][j]为以大于等于i的数字开头,(当前串的最后一段)以第j个数字结尾的最长序列长度,然后做快速幂.这个状态很巧妙,考虑到a的取值范围在300以内,可以把值作为状态,但是实际的n值更小,这样的状态表示更有效率(如果状态开到300可能会t).最后,对于这种多段问题,矩阵解法表示的一定是在某种约束条件下,最后一段的状态(这样才能dp),由于对这一点理解不清所以看了挺长时间..

/*
* @author:  Cwind
*/
#include <bits/stdc++.h>
#define pb push_back
#define se second
#define fs first
#define sq(x) (x)*(x)
#define eps (5e-12)
#define LB lower_bound
#define IINF (1<<29)
#define LINF (1ll<<59)
#define INF (1000000000)
#define bk back()
#define PB pop_back
#define clr(x) memset((x),0,sizeof (x));
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;

const int maxn=105;
int val[maxn];
int n,t;
int tmp[maxn][maxn];
struct Matirx{
    int a[maxn][maxn];
    Matirx(){
        clr(a);
    }
    void multi(const Matirx &C){
        clr(tmp);
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                tmp[i][j]=-INF;
                for(int k=0;k<n;k++){
                    tmp[i][j]=max(tmp[i][j],a[i][k]+C.a[k][j]);
                }
            }
        }
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                a[i][j]=tmp[i][j];
    }
};
Matirx qpow(Matirx a,int p){
    Matirx ans,tmp;
    while(p){
        if(p&1) ans.multi(a);
        p>>=1;
        tmp=a;
        a.multi(tmp);
    }
    return ans;
}
int mat[maxn][maxn];
int main(){
    //freopen("/home/files/CppFiles/in","r",stdin);
    //freopen("test.in","r",stdin);
    //freopen("test.out","w",stdout);
    cin>>n>>t;
    for(int i=0;i<n;i++){
        scanf("%d",&val[i]);
    }
    for(int s=0;s<n;s++){
        for(int i=0;i<n;i++){
            if(val[i]<val[s]){
                mat[s][i]=-INF;
            }else{
                mat[s][i]=1;
                for(int j=0;j<i;j++){
                    if(val[j]<=val[i])
                        mat[s][i]=max(mat[s][i],mat[s][j]+1);
                }
            }
        }
    }
    Matirx A;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            A.a[i][j]=mat[i][j];
        }
    }
    Matirx ans=qpow(A,t);
    int aa=0;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            aa=max(aa,ans.a[i][j]);
        }
    }
    cout<<aa<<endl;
    return 0;    
}
View Code

 6 CF 584E

题目:交换两个数字的花费为这两个数字的位置差,求把一个排列变成另一个排列的最小花费。

思路:先把排列转换成1~n的排列,要吧这个序列转换成顺序排列。做法是从大到小,把没到位置的最大数往后移,并保证不产生多余的花销(不会把一个较大的数移动到它位置的左边)。通过指针移动操作一个数的复杂度是O(n)。这种移动是必然存在满足条件的操作的,因为位置在x之后的数字有n-x个,并且大于x的数字有n-x个,但是由于n在位置x,所以由容斥可知必然存在要求的数字。

/*
* @author:  Cwind
*/
#include <bits/stdc++.h>
#define pb push_back
#define se second
#define fs first
#define sq(x) (x)*(x)
#define eps (5e-12)
#define LB lower_bound
#define IINF (1<<29)
#define LINF (1ll<<59)
#define INF (1000000000)
#define bk back()
#define PB pop_back
#define clr(x) memset((x),0,sizeof (x));
using namespace std;
typedef int ll;
typedef pair<ll,ll> P;

const int maxn=2005;
int n;
int a[maxn],b[maxn];
vector<P> op;
int main(){
//    freopen("/home/slyfc/CppFiles/in","r",stdin);
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n;i++){
        int x;
        scanf("%d",&x);
        b[x]=i;
    }
    for(int i=1;i<=n;i++){
        a[i]=b[a[i]];
    }
    ll cost=0;
    for(int i=n;i>=1;i--){
        int pos;
        for(int j=1;j<=n;j++){
            if(a[j]==i){
                pos=j;
                break;
            }
        }
        while(pos<i){
            int t=pos+1;
            while(a[t]>pos) t++;
            cost+=abs(pos-t);
            swap(a[pos],a[t]);
            op.pb(P(pos,t));
            pos=t;
        }
    }
    cout<<cost<<endl;
    cout<<op.size()<<endl;
    for(int i=0;i<op.size();i++){
        printf("%d %d\n",op[i].fs,op[i].se);
    }
    return 0;    
}
View Code

 7 CF 587B

题目:给出由a串重复若干次形成的数列b,求数列b中的长度小于等于k的不降子序列的数量。

思路:由于限定了相邻两项不在同一个a里,所以这题就很简单了(然而比赛的时候脑残居然写挂了。。。。。),统计的时候由于顺序关系,可以用树状数组求和。当然dp的时候完全可以先把a排序,然后求前缀和。

/*
* @author:  Cwind
*/
#include <bits/stdc++.h>
#define pb push_back
#define se second
#define fs first
#define sq(x) (x)*(x)
#define eps (5e-12)
#define LB lower_bound
#define IINF (1<<29)
#define LINF (1ll<<59)
#define INF (1000000000)
#define bk back()
#define PB pop_back
#define clr(x) memset((x),0,sizeof (x));
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;

const ll mod=1e9+7;
const int maxn=1e6+3000;
ll n,l,k;
int h;
int lim;
struct BIT{
    ll a[maxn];
    ll sum(int p){
        ll ans=0;
        while(p>0){
            ans=(ans+a[p])%mod;
            p-=p&-p;
        }
        return ans;
    }
    void add(int p,ll x){
        while(p<lim){
            a[p]=(a[p]+x)%mod;
            p+=p&-p;
        }
    }
    void clear(){
        for(int i=0;i<=lim;i++){
            a[i]=0;
        }
    }
}B[2];
int dd[maxn];
int a[maxn];
int main(){
    freopen("/home/slyfc/CppFiles/in","r",stdin);
    cin>>n>>l>>k;
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
        dd[i]=a[i];
    }
    sort(dd,dd+n);
    h=unique(dd,dd+n)-dd;
    lim=h+300;
    ll ans=l%mod;
    if(l<=n){
        cout<<l%mod<<endl;
        return 0;
    }
    for(int i=0;i<n;i++){
        a[i]=lower_bound(dd,dd+h,a[i])-dd+1;
    }
    bool f=0;
    for(int i=0;i<n;i++){
        B[f].add(a[i],1);
    }
    ll xx=min(k,l/n);
    for(int len=2;len<=xx;len++){
        f^=1;
        B[f].clear();
        for(int i=0;i<n;i++){
            B[f].add(a[i],B[f^1].sum(a[i]));
            if(l%n!=0&&i+1==l%n) ans=(ans+B[f].sum(lim))%mod;
        }
        ans=(ans+B[f].sum(lim)*(l/n%mod-len+1+3*mod)%mod)%mod;
    }
    if(l%n!=0&&k>xx&&xx<=l/n+1){
        f^=1;
        B[f].clear();
        for(int i=0;i<l%n;i++){
            B[f].add(a[i],B[f^1].sum(a[i]));
        }
        ans=(ans+B[f].sum(lim))%mod;
    }
    cout<<ans<<endl;
    return 0;    
}
View Code

 8 CF 587C

题目:一棵树的每个节点有若干值,询问某两点之间路径上的最小的a个值。

思路:裸倍增lca。

/*
* @author:  Cwind
*/
#include <bits/stdc++.h>
#define pb push_back
#define se second
#define fs first
#define sq(x) (x)*(x)
#define eps (5e-12)
#define LB lower_bound
#define IINF (1<<29)
#define LINF (1ll<<59)
#define INF (1000000000)
#define bk back()
#define PB pop_back
#define clr(x) memset((x),0,sizeof (x));
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;


void print(vector<int> &a){
    for(int i=0;i<a.size();i++){
        printf("%d ",a[i]);
    }
    puts("");
}
const int maxn=1e5+300;
int n,m,q;
vector<int> G[maxn];
vector<int> ls[maxn][18];
int pa[maxn][18];
int dep[maxn];
void dfs(int v,int f=0,int d=0){
    pa[v][0]=f;dep[v]=d;
    for(int i=0;i<G[v].size();i++){
        int u=G[v][i];
        if(u==f) continue;
        dfs(u,v,d+1);
    }
}
int mergelim=10;
void merge(vector<int> &a,vector<int> &b,vector<int> &c){
    int h1=0,h2=0;
    while(h1<a.size()&&h2<b.size()&&c.size()<mergelim){
        if(a[h1]<b[h2]){
            if(!c.size()||c.bk!=a[h1]){
                c.pb(a[h1]);
            }
            h1++;
        }else{
            if(!c.size()||c.bk!=b[h2]){
                c.pb(b[h2]);
            }
            h2++;
        }
    }
    while(h1<a.size()&&c.size()<mergelim){
        if(!c.size()||c.bk!=a[h1]){
            c.pb(a[h1]);
        }
        h1++;
    }
    while(h2<b.size()&&c.size()<mergelim){
        if(!c.size()||c.bk!=b[h2]){
            c.pb(b[h2]);
        }
        h2++;
    }
}
void cal(){
    dfs(1);
    for(int k=1;k<18;k++){
        for(int i=1;i<=n;i++){
            pa[i][k]=pa[pa[i][k-1]][k-1];
            merge(ls[i][k-1],ls[pa[i][k-1]][k-1],ls[i][k]);
        }
    }
}
vector<int> ans,tmp;
void lca(int x,int y){
    ans.clear();
    if(dep[x]<dep[y]) swap(x,y);
    for(int k=0;k<18;k++){
        if((dep[x]-dep[y])&(1<<k)){
            tmp=ans;
            ans.clear();
            merge(tmp,ls[x][k],ans);
            x=pa[x][k];
        }
    }
    if(x==y){
        tmp=ans;
        ans.clear();
        merge(tmp,ls[x][0],ans);
        return;
    }
    for(int k=18-1;k>=0;k--){
        if(pa[x][k]!=pa[y][k]){
            tmp=ans;
            ans.clear();
            merge(tmp,ls[x][k],ans);
            tmp=ans;
            ans.clear();
            merge(tmp,ls[y][k],ans);
            x=pa[x][k];
            y=pa[y][k];
        }
    }
    tmp=ans;
    ans.clear();
    merge(tmp,ls[x][1],ans);
    tmp=ans;
    ans.clear();
    merge(tmp,ls[y][0],ans);
}
int main(){
    //freopen("/home/slyfc/CppFiles/in","r",stdin);
    cin>>n>>m>>q;
    for(int i=0;i<n-1;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        G[x].pb(y);
        G[y].pb(x);
    }
    for(int i=1;i<=m;i++){
        int x;
        scanf("%d",&x);
        if(ls[x][0].size()<10) ls[x][0].pb(i);
    }

    cal();
    while(q--){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        lca(x,y);
        printf("%d ",min((int)ans.size(),z));
        for(int i=0;i<min((int)ans.size(),z);i++){
            printf("%d ",ans[i]);
        }
        puts("");
    }
    return 0;    
}
View Code

9  CF 589G

题目:离线查询一个数列的前n项中比x大的数之和。

思路:这个题首先要离线排序,天和人都按时间降序,然后注意到如果满足较大的d,那么较小的d一定满足,然后用两个bit维护和和个数。

/*
* @author:  Cwind
*/
///#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 bk back()
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps (1e-7)
#define IINF (1<<29)
#define LINF (1ll<<59)
#define INF (1000000000)
#define FINF (1e3)
#define clr(x) memset((x),0,sizeof (x))
#define cp(a,b) memcpy((a),(b),sizeof (a))
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<int,int> P;

const int maxn=2e5+300;
int n,m;
P t[maxn];
pair<P,int> c[maxn];
struct BIT{
    ll a[maxn];
    ll sum(int p){
        ll ans=0;
        while(p>0){
            ans+=a[p];
            p-=p&-p;
        }
        return ans;
    }
    void add(int p,int x){
        while(p<maxn){
            a[p]+=x;
            p+=p&-p;
        }
    }
}A,B;
int ans[maxn];
int main(){
    //freopen("/home/slyfc/CppFiles/in","r",stdin);
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        scanf("%d",&t[i].fs);
        t[i].se=i;
    }
    for(int i=1;i<=n;i++){
        int d,r;
        scanf("%d%d",&d,&r);
        c[i].fs.fs=d;c[i].fs.se=r;
        c[i].se=i;
    }
    sort(t+1,t+m+1,greater<P>());
    sort(c+1,c+n+1,greater<pair<P,int> >());
    int pt=1;
    for(int i=1;i<=n;i++){
        int d=c[i].fs.fs,R=c[i].fs.se;
        while(t[pt].fs>=d&&pt<=m){
            B.add(t[pt].se,t[pt].fs);
            A.add(t[pt].se,1);
            pt++;
        }
        int l=0,r=m;
        while(r-l>1){
            int mid=(l+r)/2;
            if(B.sum(mid)-d*A.sum(mid)>=R){
                r=mid;
            }else{
                l=mid;
            }
        }
        if(B.sum(r)-d*A.sum(r)>=R) ans[c[i].se]=r;
        else ans[c[i].se]=0;
    }
    for(int i=1;i<=n;i++){
        cout<<ans[i]<<" ";
    }
    return 0;

}
View Code

 

posted @ 2015-09-28 14:09  PlusSeven  阅读(158)  评论(0编辑  收藏  举报