2016-2017 ACM-ICPC Asia-Bangkok Regional Contest

B:数学公式

先直接毙掉DP打表,然后就是数学题了,容斥加隔板法。推出公式:将n个球放入m给盒子,每个盒子最多p个的公式是

why是这样?不知道啊!!然后注意这个公式组合数的参数是可以为负数的,特判一下。

#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
const long long mod=1e9+7;
long long fac[12010],rev[12010];
long long qp(long long p,long long q)
{
    int cnt=1;
    while(q>0)
    {
        if(q%2==1) cnt=(cnt*p)%mod;
        q/=2;
        p=(p*p)%mod;
    }
    return cnt%mod;
}

long long C(long long a,long long b)
{
    //cout<<a<<" "<<b<<endl;
    if(a<0||b<0) return 0;
    long long flag=fac[a];
    return ((fac[a]*rev[b])%mod*rev[a-b])%mod;
}

long long n,k;

long long solve(long long sum,long long num)
{
    if(sum==0) return 1;
    long long cnt=0;
    int i,j;
    for(i=0;i<=num;i++)
    {
        long long a=C(num,i),b=C(sum-k*i-i+num-1,sum-k*i-i);
        long long flag=(C(num,i)*C(sum-k*i-i+num-1,sum-k*i-i))%mod;
        //cout<<flag<<endl;
        if(i%2==1) flag=(mod-flag)%mod;
        cnt=(cnt+flag)%mod;
    }
    return cnt;
}

int main()
{
    int i,j;
    fac[0]=1;
    rev[0]=1;
    for(i=1;i<=12005;i++)
    {
        fac[i]=(fac[i-1]*i)%mod;
        rev[i]=qp(fac[i],mod-2);
        //if(i<=5)cout<<fac[i]<<" "<<rev[i]<<endl;
    }
    k=1;
    while(scanf("%lld%lld",&n,&k))
    {
        if(n==0&&k==0) break;
        long long ans=0;
        //cout<<solve(2,2)<<endl;
        for(i=0;i<=k;i++)
        {
            ans=(ans+(solve((n-1)*i,n-1))%mod)%mod;
        }
        printf("%lld\n",(ans*n)%mod);
    }
    return 0;
}
View Code

E:dfs序+主席树

按边权跑一遍dfs序得到数组a[i],将这个数组用主席树维护:设T[i]为在dfs序中标号为i的主席树的根,若a[i]>0,则在位置a[i]进行+1,否则-1.

对于一条树链x~y,由dfs序可得这条树链在主席树上可以用T[x]+T[y]-2*T[lca(x,y)]来表示,故查询时分情况查询第k大即可获得中位数。

  1 #include<map>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cstdlib>
  5 #include<algorithm>
  6 
  7 #define maxn 200000+5
  8 
  9 using namespace std;
 10 
 11 struct Ha_Tree{
 12     int l,r;
 13     int sum;
 14 }tr[maxn<<5];
 15 
 16 struct Edge{
 17     int u,v,c,nxt;
 18 }e[maxn<<1];
 19 
 20 int a[maxn],root[maxn],head[maxn],dep[maxn],fa[maxn][20],pos[maxn];
 21 int q,n,len,ind,id,k;
 22 
 23 void addedge(int x,int y,int c){
 24     e[id]=(Edge){x,y,c,head[x]},head[x]=id++;
 25     e[id]=(Edge){y,x,c,head[y]},head[y]=id++;
 26 }
 27 
 28 void Build(int l,int r,int &rt){
 29     rt=++ind;
 30     tr[rt].sum=0;
 31     if(l==r) return;
 32     int mid=(l+r)>>1;
 33     Build(l,mid,tr[rt].l);
 34     Build(mid+1,r,tr[rt].r);
 35 }
 36 
 37 void Update(int l,int r,int last,int &k,int pos,int val){
 38     k=++ind;
 39     tr[k]=tr[last]; tr[k].sum+=val;
 40     if(l==r) return;
 41     int mid=(l+r)>>1;
 42     if(pos<=mid) Update(l,mid,tr[last].l,tr[k].l,pos,val);
 43     else Update(mid+1,r,tr[last].r,tr[k].r,pos,val);
 44 }
 45 
 46 int Query(int l,int r,int s,int t,int lca,int k){
 47     if(l==r) return r;
 48     int mid=(l+r)>>1;
 49     int cnt=tr[tr[t].l].sum+tr[tr[s].l].sum-2*tr[tr[lca].l].sum;
 50     if(k<=cnt) return Query(l,mid,tr[s].l,tr[t].l,tr[lca].l,k);
 51     else return Query(mid+1,r,tr[s].r,tr[t].r,tr[lca].r,k-cnt);
 52 }
 53 
 54 void dfs(int x,int p){
 55     dep[x]=dep[p]+1;
 56     for(int i=head[x];i!=-1;i=e[i].nxt)
 57         if(e[i].v!=p){
 58             a[++len]=e[i].c;
 59             fa[e[i].v][0]=x;
 60             pos[e[i].v]=len;
 61             
 62             dfs(e[i].v,x);
 63             
 64             a[++len]=-e[i].c;
 65         }
 66 }
 67 
 68 void init(){
 69     for(int i=1;i<20;i++)
 70         for(int j=1;j<=n;j++)
 71             fa[j][i]=fa[fa[j][i-1]][i-1];
 72 }
 73 
 74 int LCA(int x,int y){
 75     if(dep[x]>dep[y]) swap(x,y);
 76     for(int i=19;i>=0;i--)
 77         if((dep[y]-dep[x])&(1<<i)) y=fa[y][i];
 78     if(x==y) return x;
 79     for(int i=19;i>=0;i--)
 80         if(fa[x][i]!=fa[y][i]){
 81             x=fa[x][i]; y=fa[y][i];
 82         }
 83     return fa[x][0];
 84 }
 85 
 86 int da(int x,int s){
 87     for(int i=19;i>=0;i--)
 88         if(s&(1<<i)) x=fa[x][i];
 89     return x;
 90 }
 91 
 92 int main(){
 93     int T;
 94     scanf("%d",&T);
 95     while(T--){
 96         int lim=0;
 97         scanf("%d",&n);
 98         id=len=ind=0;
 99         memset(a,0,sizeof(a)); memset(tr,0,sizeof(tr));
100         memset(root,0,sizeof(root));
101         memset(head,-1,sizeof(head));
102         for(int i=1;i<n;i++){
103             int x,y,c;
104             scanf("%d%d%d",&x,&y,&c);
105             addedge(x,y,c); lim=max(lim,c);
106         }
107         a[++len]=0; pos[1]=1;
108         dfs(1,0); init();
109         Build(1,lim,root[0]);
110         for(int i=1;i<=len;i++){
111             if(a[i]>0) Update(1,lim,root[i-1],root[i],a[i],1);
112             else if(a[i]<0) Update(1,lim,root[i-1],root[i],-a[i],-1);
113             else Update(1,lim,root[i-1],root[i],1,0);
114         }
115         scanf("%d",&q);
116         for(int i=1;i<=q;i++){
117             int x,y,l,sum;
118             int rtx,rty,rtl;
119             scanf("%d%d",&x,&y);
120             l=LCA(x,y);
121             sum=dep[x]+dep[y]-2*dep[l];
122             rtx=root[pos[x]]; rty=root[pos[y]]; rtl=root[pos[l]];
123             if(sum%2==0){
124                 printf("%.1f\n",(Query(1,lim,rtx,rty,rtl,sum/2)+Query(1,lim,rtx,rty,rtl,sum/2+1))*0.5);
125             }
126             else{
127                 printf("%.1f\n",Query(1,lim,rtx,rty,rtl,sum/2+1)*1.0);
128             }
129         
130         }
131     }
132     return 0;
133 }
View Code

F:(Trie树+树上博弈)

树上删边游戏博弈结论:叶子节点的SG值为0,非叶子节点的SG值为子节点SG值+1的异或和。

每次加入一个字符串便在Trie树上扩展一条链,更新链上节点的SG值(注意消除这条链上之前节点的影响)

View Code

G:矩阵快速幂

首先找规律,发现数量规律是一个斐波拉契数列,长度为k的串,长度为f(k+1)。之后求[L,R]区间内的和,于是可以想到利用矩阵快速幂求前缀和,将2*2的斐波拉契数列系数矩阵增加一维求和。注意初始的是f(k+1)。所以构造矩阵有一些奇特(见代码)

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#include <string>
#include <map>
using namespace std;
const long long mod = 1e9 + 7;
struct Matrix
{
    long long data[35][35];
    int col, row;
    Matrix() {}
    Matrix(int _row, int _col)
    {
        row = _row;
        col = _col;
        memset(data, 0, sizeof(data));
    }
    const long long * operator [] (int row) const         //жидиЯТБъдЫЫуЗћ
    {
        return data[row];
    }
    long long * operator [] (int row)
    {
        return data[row];
    }
};
Matrix operator * (const Matrix &m1, const Matrix &m2)
{
    Matrix ans_met(m1.row, m2.col);
    int i, j, k;
    for (i = 0; i < m1.row; i++)
    {
        for (j = 0; j < m2.col; j++)
        {
            for (k = 0; k < m1.col; k++)
            {
                ans_met[i][j] = (ans_met[i][j] + (m1[i][k] * m2[k][j]) % mod) % mod;
            }
        }
    }
    return ans_met;
}
Matrix operator ^(const Matrix &mm, long long q)
{
    Matrix ans_met(mm.row, mm.col);
    Matrix ret(mm.row, mm.col);
    int i, j;
    for (i = 0; i < ans_met.row; i++)
    {
        for (j = 0; j < ans_met.col; j++) ret[i][j] = mm[i][j];
        ans_met[i][i] = 1;
    }
    while (q > 0)
    {
        if (q % 2 == 1) ans_met = ans_met * ret;
        ret = ret * ret;
        q /= 2;
    }
    return ans_met;
}
Matrix operator + (const Matrix &m1, const Matrix &m2)
{
    Matrix ans_met(m1.row, m2.col);
    int i, j;
    for (i = 0; i < m1.row; i++)
    {
        for (j = 0; j < m1.col; j++)
        {
            ans_met[i][j] = (m1[i][j] + m2[i][j]) % mod;
        }
    }
    return ans_met;
}

Matrix fab(2, 2), mat(3, 3);

long long solve(long long q)
{
    q++;              //注意要移位
    Matrix tmp(3, 3);
    tmp = mat ^ q;
    return tmp[0][2]-1;
}

int main()
{
    int i, j;
    int t;
    scanf("%d", &t);
    int cases = 1;
    while (t--)
    {
        long long l, r, k;
        scanf("%lld%lld%lld", &l, &r, &k);
        if (l%k != 0) l += k - (l%k);      //处理余数
        if (r%k != 0) r -= r % k;
        fab[0][0] = 1;
        fab[0][1] = 1;
        fab[1][0] = 1;
        fab[1][1] = 0;
        fab = fab ^ k;

        mat[0][0] = fab[0][0];         //构造求和矩阵
        mat[0][1] = fab[0][1];
        mat[1][0] = fab[1][0];
        mat[1][1] = fab[1][1];
        mat[0][2] = 1;
        mat[1][2] = 1;
        mat[2][0] = 0;
        mat[2][1] = 0;
        mat[2][2] = 1;
        long long le = solve(l / k - 1), ri = solve(r / k);
        long long ans = ((ri - le) % mod + mod) % mod;
        printf("Case %d: ", cases++);
        printf("%d\n", ans);
    }
    return 0;
}
View Code

 I:LCA+倍增

问题的关键是确定当前根与查询子树的根节点的关系,确定关系后可以直接在原树中用删减的方式得到新树里待查询子树的大小。具体方式如下:

1.查询点x与当前根rt的lca的深度小于x的深度(rt不在x的子树内):答案为原树中x的子树大小size[x]

2.查询点x与当前根rt的lca为x(rt在x的子树内):将rt倍增至x的儿子t处,答案为n-size[t]

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<algorithm>
 5 
 6 #define maxn 100000+5
 7 
 8 using namespace std;
 9 
10 struct Edge{
11     int u,v,nxt;
12 }e[maxn<<1];
13 
14 int head[maxn],fa[maxn][20],dep[maxn],sz[maxn];
15 int n,m,root,ind;
16 
17 void addedge(int x,int y){
18     e[ind]=(Edge){x,y,head[x]},head[x]=ind++;
19     e[ind]=(Edge){y,x,head[y]},head[y]=ind++;
20 }
21 
22 void dfs(int x,int p){
23     dep[x]=dep[p]+1; sz[x]=1;
24     for(int i=head[x];i!=-1;i=e[i].nxt)
25         if(e[i].v!=p){
26             fa[e[i].v][0]=x; 
27             dfs(e[i].v,x);
28             sz[x]+=sz[e[i].v];
29         }
30 }
31 
32 void init(){
33     for(int i=1;i<20;i++)
34         for(int j=1;j<=n;j++)
35             fa[j][i]=fa[fa[j][i-1]][i-1];
36 }
37 
38 int LCA(int x,int y){
39     if(dep[x]>dep[y]) swap(x,y);
40     for(int i=19;i>=0;i--)
41         if((dep[y]-dep[x])&(1<<i)) y=fa[y][i];
42     if(x==y) return x;
43     for(int i=19;i>=0;i--)
44         if(fa[x][i]!=fa[y][i]){
45             x=fa[x][i]; y=fa[y][i];
46         }
47     return fa[x][0];
48 }
49 
50 int da(int x,int s){
51     for(int i=19;i>=0;i--)
52         if(s&(1<<i)) x=fa[x][i];
53     return x;    
54 }
55 
56 int main(){
57     int T,kase=1;
58     scanf("%d",&T);
59     while(T--){
60         memset(head,-1,sizeof(head));
61         memset(fa,0,sizeof(fa));
62         memset(dep,0,sizeof(dep));
63         
64         printf("Case #%d:\n",kase++);
65         scanf("%d%d%d",&n,&m,&root); ind=0;
66         for(int i=1;i<n;i++){
67             int x,y;
68             scanf("%d%d",&x,&y);
69             addedge(x,y);
70         }
71         dfs(root,0); 
72         init();
73         for(int i=1;i<=m;i++){
74             int op,x;    
75             scanf("%d%d",&op,&x);
76             if(op==1){
77                 int lca=LCA(x,root);
78                 if(dep[x]>dep[lca]) printf("%d\n",sz[x]);
79                 else{
80                     int p=da(root,dep[root]-dep[x]-1);
81                     printf("%d\n",n-sz[p]);
82                 }
83             }
84             else root=x;
85         }
86     }
87     return 0;
88 }
View Code

 L:模拟+dfs

由于题目保证可以确定所有坐标,故建立双向边构图,dfs一遍后将坐标调整进[-1e9,1e9]区间内即可。

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<cctype>
 5 #include<algorithm>
 6 
 7 #define maxn 100000+5
 8 #define maxm 1000000+5
 9 
10 using namespace std;
11 
12 struct Edge{
13     int u,v,dx,dy,nxt;
14 }e[maxm<<1];
15 
16 int x[maxn],y[maxn],head[maxn],ok[maxn],du[maxn];
17 int n,m,ind;
18 
19 void addedge(int x,int y,int dx,int dy){
20     e[ind]=(Edge){x,y,dx,dy,head[x]},head[x]=ind++;
21     e[ind]=(Edge){y,x,-dx,-dy,head[y]},head[y]=ind++;
22 }
23 
24 void dfs(int xx,int yy,int p){
25     ok[p]=1; x[p]=xx; y[p]=yy;
26     for(int i=head[p];i!=-1;i=e[i].nxt)
27         if(!ok[e[i].v]){
28             dfs(xx+e[i].dx,yy+e[i].dy,e[i].v);
29         }
30 }
31 
32 int main(){
33     scanf("%d%d",&n,&m);
34     memset(head,-1,sizeof(head));
35     for(int i=1;i<=m;i++){
36         int a,b,dx,dy;
37         scanf("%d%d%d%d",&a,&b,&dx,&dy);
38         addedge(a,b,dx,dy);
39     }
40     dfs(0,0,1);
41     int mx=INT_MAX,my=INT_MAX,xm=INT_MIN,ym=INT_MIN;
42     for(int i=1;i<=n;i++){
43         mx=min(x[i],mx); my=min(y[i],my);
44         ym=max(y[i],ym); xm=max(x[i],xm);
45     }
46     if(xm>1e9){
47         for(int i=1;i<=n;i++){
48             x[i]-=xm-1e9+1;
49         }
50     }
51     if(ym>1e9){
52         for(int i=1;i<=n;i++){
53             y[i]-=ym-1e9+1;
54         }
55     }
56     if(mx<-1e9){
57         for(int i=1;i<=n;i++){
58             x[i]+=-1e9-mx+1;
59         }
60     }
61     if(my<-1e9){
62         for(int i=1;i<=n;i++){
63             y[i]+=-1e9-my+1;
64         }
65     }
66     for(int i=1;i<=n;i++){
67         printf("%d %d\n",x[i],y[i]);
68     }
69     return 0;
70 }
View Code

 

posted @ 2018-06-03 16:34  Hetui  阅读(278)  评论(0编辑  收藏  举报