码迷,mamicode.com
首页 > 其他好文 > 详细

【学长虐学弟欢乐赛系列】Round4

时间:2015-03-03 16:42:13      阅读:180      评论:0      收藏:0      [点我收藏+]

标签:随便搞搞

第四套题
出题人:Bakser
神犇地址:Bakser学长的blog
试题下载地址
数据下载地址
T1 LICS
有一天 Bakser 在做数学题,对于一道题他验算了 n 遍,得到了 n
个结果。无聊的他将这 n 个结果排成了一个圆环,无聊的他又想求这
个环的最长上升子序列。
请你回答这个无聊的问题。
所谓环的上升子序列,指的是从某个位置开始, 按照顺时针顺序
读这个环,得到一个线性序列,它的上升子序列也是这个环的上升子
序列。最长上升子序列是它们中最长的一个。
输入格式:
第一行一个数 n,表示有多少个元素。 第二行 n 个数, ai 表示从位
置 1 开始按顺时针顺序读这个环读到的第 i 个数是什么。
输出格式:
一行,一个数表示最长上升子序列长度。
样例输入:
5
1 2 3 4 5
样例输出:
5
数据范围:
对于 30%的数据, n1000
对于 100%的数据, n50000,1ai109
所有数据均为随机生成
专门从国外论文上找来题虐我们真是…跪了
全场比赛就靠这个题骗了30分233
还好8s时限写个普通暴力也能得分
标算太神
技术分享
技术分享
技术分享
听说美国80年代开始研究这东西结果一样的O(nnlogn)算法一场比赛被神犇们搞出来了Orz

//std by Bakser
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int N(5e4+200);
typedef pair<int,int> pii;
int n,a[N],b[N],x[N],t[N],top,f1[N],f2[N];
inline int find(int s){
    int len(0);
    memset(x,0x7f,sizeof(x));x[0]=0;
    for(int i(0);i<n;i++){
        int pos;
        if(i<=n-s)pos=s+i;
        else pos=i-n+s;
        int p=lower_bound(x,x+len+1,a[pos])-x;
        while(x[p]>=a[pos])p--;
        if(a[pos]<x[p+1])
            x[p+1]=a[pos],t[p+1]=pos,len+=(p==len);
    }
    return len;
}
inline int b_s(int l,int r,int val){
    int res=0;
    for(;l<=r;){
        int mid(l+r>>1);
        if(x[mid]>val)l=mid+1,res=max(res,mid);
        else r=mid-1;
    }
    return res;
}
int main(){
    freopen("LICS.in","r",stdin);
    freopen("LICS.out","w",stdout);
    scanf("%d",&n);
    for(int i(1);i<=n;i++)scanf("%d",a+i);
    int len=find(1),ans=len;
    for(int i(1);i<=len;i++){
        int l=0,p(0),last(0);
        memset(f1,0,sizeof(f1));
        memset(f2,0,sizeof(f2));
        memset(x,0x7f,sizeof(x));x[0]=0;
        for(int j(0);j<n;j++){
            p=(t[i]+j)%n;
            if(!p)p=n;
            f1[p]=0;
            if(a[p]>=a[t[i]]){
                int t=lower_bound(x,x+l+1,a[p])-x;
                while(x[t]>=a[p])t--;
                if(a[p]<x[t+1])
                    x[t+1]=a[p],l+=(t==l);
                f1[p]=t+1;
            }
            f1[p]=max(f1[p],f1[last]);
            last=p;
        }
        l=0;memset(x,0,sizeof(x));x[0]=0x7fffffff;
        for(int j(0);j<n;j++){
            p=(t[i]-j+n)%n;
            if(!p)p=n;
            f2[p]=0;
            if(a[p]<=a[t[i]]){
                int t=b_s(0,l,a[p]);
                if(a[p]>x[t+1])
                    x[t+1]=a[p],l+=(t==l);
                f2[p]=t+1;
            }
            f2[p]=max(f2[p],f2[last]);
            last=p;
        }
        for(int i(1);i<=n;i++)
            ans=max(ans,f1[i]+f2[i%n+1]-1);
    }
    printf("%d\n",ans);
    return 0;
}

T2 Tree
Bakser 得到了一棵无根树,每个节点上有一个权值, 他决定在这
棵树上逗比。设这棵树上从 u 到 v 的路径长度为 l,这条路径上的点
权 构 成 一 个 序 列 {z0,? z1…zl } 。 则 这 条 路 径 的 权 值 定 义 为
z0 × k0 +? z1 ×? k1 +?…?+? zl ×? kl。如果这条路径的权值 MOD Y 等于 X,则
称这条路径为逗比的。否则称它为高冷的。
他想出了一个结论,如果路径(u,t)和(t,v)是逗比的,则路径(u,v)一
定是逗比的。如果路径(u,t)和(t,v)是高冷的,则路径(u,v)一定是高冷的。
显然这个结论是错的,但高冷(dòu bī)的他才不会承认这个结论是
错的,请你统计对于给定的树, 有多少个满足这个结论的三元组(u,t,v)。
输入格式:
第一行四个数 n、 Y、 k、 X, n 是这棵树的点数, Y、 k、 X 的含义如
题目描述所示。
第二行 n 个数: v1 、 v2……vn,。 vi 表示节点 i 的权值。
接下来 n-1 行, 每行两个数 u、 v, 表示存在一条从 u 到 v 的无向
边。
输出格式:
一行,一个数表示满足题目条件的三元组数目 。
样例输入:
3 5 2 1
4 3 1
1 2
2 3
样例输出:
14
数据范围:
对于 30%的数据, n100
对于 60%的数据, n1000
对于 100%的数据, n100000 , 2? Y?? 109; 1?? k <? Y; 0?? X <? Y
考试时候看出来是点分治了但是不会点分治= =
后来写了链剖代替
再后来就写残了
看来链剖果然不是正确姿势QAQ
枚举三元组太蛋疼= =标算的代替方法看得我醉了
技术分享
100分算法就是在这个基础上加上了点分治而已
技术分享

//std by Bakser    点分治好可怕比我链剖还长
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <climits>
#include <vector>
#include <map>
using namespace std;
const int N(1e5+200);
typedef long long LL;
typedef vector<int>::iterator iter;
#define MAX(a,b) (((a)>(b))?(a):(b))
template<class T>
inline void read(T &x){
    char c=0;
    for(;c<‘0‘||c>‘9‘;c=getchar());
    for(x=0;c>=‘0‘&&c<=‘9‘;c=getchar())x=(x*10+(c-‘0‘));
}
int n,w[N],X,K,MOD,point[N],sz[N],f[N],in[N],ou[N],up[N],need[N],down[N],rot,pw[N],inv[N],q[N],top,dep[N];
struct hh{int next,v;}edges[N*2];
bool vis[N];
LL ans(0);
vector<int> son[N];
map<int,int>from,to;
inline void addedge(int u,int v){
    static int en(0);
    edges[++en].next=point[u];point[u]=en;
    edges[en].v=v;
}
inline LL power(LL x,LL k){
    LL res(1);
    for(;k;k>>=1){
        if(k&1)res=res*x%MOD;
        x=x*x%MOD;
    }
    return res;
}
inline void dfs_sz(int x,int fa){
    sz[x]=1;f[x]=0;q[++top]=x;
    for(int i(point[x]);i;i=edges[i].next)
        if(!vis[edges[i].v]&&edges[i].v!=fa){
            int v(edges[i].v);
            dfs_sz(v,x);
            sz[x]+=sz[v];
            f[x]=MAX(f[x],sz[v]);
        }
}
inline int find_focus(int x){
    top=0;dfs_sz(x,0);
    int res,tmp(INT_MAX);
    for(int i(1);i<=top;i++){
        int t=MAX(sz[x]-sz[q[i]],f[q[i]]);
        if(t<tmp)tmp=t,res=q[i];
    }
    return res;
}
inline void dfs(int x,int fa){
    for(int i(point[x]);i;i=edges[i].next)
        if(!vis[edges[i].v]&&edges[i].v!=fa){
            int v(edges[i].v);
            dep[v]=dep[x]+1;
            up[v]=((LL)up[x]*(LL)K%MOD+(LL)w[v])%MOD;
            down[v]=(down[x]+(LL)w[v]*(LL)pw[dep[v]])%MOD;
            dfs(v,x);
        }
}
inline void get_sons(int x,int fa,int anc){
    son[anc].push_back(x);
    for(int i(point[x]);i;i=edges[i].next)
        if(!vis[edges[i].v]&&edges[i].v!=fa)
            get_sons(edges[i].v,x,anc);
}
inline int ask(map<int,int> &m,int x){
    if(m.count(x))return m[x];
    return 0;
}
inline void add(map<int,int> &m,int x,int f){
    if(m.count(x))m[x]+=f;
    else m[x]=f;
}
inline void calc(){
    from.clear();to.clear();
    for(int i(1);i<=top;i++)
        for(iter j=son[q[i]].begin();j!=son[q[i]].end();j++)
            add(to,down[*j],1);
    for(int i(1);i<=top;i++){
        vector<int> &x=son[q[i]];
        for(iter j=x.begin();j!=x.end();j++)
            add(to,down[*j],-1);
        for(iter j=x.begin();j!=x.end();j++){
            ou[*j]+=ask(to,need[*j]);
            in[*j]+=ask(from,down[*j]);
        }
        for(iter j=x.begin();j!=x.end();j++)
            add(from,need[*j],1);
    }
}
inline void solve(int x){
    vis[x=find_focus(x)]=1;
    up[x]=down[x]=w[x];dep[x]=0;
    dfs(x,0);
    for(int i(1);i<=top;i++)
        if(q[i]==x){if(up[x]==X)ou[x]++,in[x]++;}
        else{
            if(up[q[i]]==X)in[x]++,ou[q[i]]++;
            if(down[q[i]]==X)ou[x]++,in[q[i]]++;
        }top=0;
    for(int i(point[x]);i;i=edges[i].next)
        if(!vis[edges[i].v]){
            int v(edges[i].v);
            q[++top]=v;son[v].clear();get_sons(v,0,v);
            for(iter j=son[v].begin();j!=son[v].end();++j)
                need[*j]=((X-up[*j]+MOD)*(LL)inv[dep[*j]]+w[x])%MOD; 
        }
    calc();
    reverse(q+1,q+top+1);
    calc();
    for(int i(point[x]);i;i=edges[i].next)
        if(!vis[edges[i].v])
            solve(edges[i].v);
}
int main(){
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    read(n);read(MOD);read(K);read(X);
    for(int i(1);i<=n;i++)read(w[i]);
    for(int i(1),u,v;i<n;i++){
        read(u);read(v);
        addedge(u,v);
        addedge(v,u);
    }
    for(int i(pw[0]=1);i<=n;i++){
        pw[i]=(pw[i-1]*(LL)K)%MOD;
        inv[i]=power(pw[i],MOD-2);
    }f[0]=INT_MAX;
    solve(1);
    for(int i(1);i<=n;i++){
        ans+=((LL)in[i]*LL(n-in[i]))<<1;
        ans+=((LL)ou[i]*LL(n-ou[i]))<<1;
        ans+=(LL)in[i]*LL(n-ou[i])+(LL)ou[i]*LL(n-in[i]);
    }
    ans=(LL)n*n*n-(ans>>1);
    cout<<ans<<endl;
    return 0;
}

T3 Circle
Bakser 是一个大蒟蒻,有一天他随手画了一个 n 个点的环状图。
他觉得只有一个环不美观就又在中间加了一个点,并从环上的 n 个点
分别向中间这个点引了一条边。
现在他想知道这个 n+1 个点的图有多少种不同的生成树。
每个点被认为是本质相同的,所以两个生成树被认为是相同的,
当且仅当这两个生成树经过旋转可以完全重合。
但良心的他觉得上面那个条件太丧病了,所以在某些数据中你不
必考虑它。
输入格式:
一行三个正整数: n,m,f。 n 表示环上的点数(即整个图一共 n+1
个点) m 表示答案对 m 取模。 f 为 1 时表示不需要考虑旋转同构,为
0 时表示需要考虑。
输出格式:
一行一个数,表示答案对 m 取模后的结果。
样例输入:
1 2333333 1
样例输出 :
1
数据范围:
数据点编号
1-3 n100 m109 f=1
4-5 n108 m109 f=1
6-7 n105 m109 f=0
8-10 n109 m109 f=0
一直想用组合数学做这个题
还是没做出来…
Sunshine神犇打表找规律过50%真是太神了OTZ
50分正确姿势Matrix-Tree定理好像大家都不爱他
递推式太神OTZ
(P.S.知道了也写不好…因为不会写矩阵乘)
同构使用群论解决
技术分享
技术分享

为什么觉得有点像→Longge的问题

//std by Bakser
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N(1e9);
const int M(40000);
int n,phi[M],prime[M],tot;
bool vis[M];
LL MOD;
inline LL mul(LL a,LL b){
    LL res=0;
    a=(a%MOD+MOD)%MOD;
    b=(b%MOD+MOD)%MOD;
    for(;b;b>>=1){
        if(b&1)res=(res+a)%MOD;
        a=(a<<1)%MOD;
    }
    return res;
}
struct Mat{
    LL a[2][2];
    inline Mat operator *(const Mat &b)const{
        Mat c;
        for(int i(0);i<2;i++)
            for(int j(0);j<2;j++){
                c.a[i][j]=0;
                for(int k(0);k<2;k++)
                    c.a[i][j]=(c.a[i][j]+mul(a[i][k],b.a[k][j]))%MOD;
            }
        return c;
    }
}base;
inline Mat power(Mat a,int k){
    Mat res;
    for(int i(0);i<2;i++)
        for(int j(0);j<2;j++)
            res.a[i][j]=(i==j);
    for(;k;k>>=1){
        if(k&1)res=res*a;
        a=a*a;
    }
    return res;
}
inline void sieve(){
    for(int i(2);i<=M;i++){
        if(!vis[i])prime[++tot]=i;
        for(int j(1);j<=tot&&i*prime[j]<=M;j++){
            vis[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
} 
inline LL get_phi(int n){
    LL res(n);
    for(int i(1);i<=tot&&prime[i]*prime[i]<=n;i++)
        if(n%prime[i]==0){
            res=res/prime[i]*(prime[i]-1);
            while(n%prime[i]==0)n/=prime[i];
        }
    if(n>1)res=res/n*(n-1);
    return res%MOD;
}
inline LL get_T(int k){
    if(k==1)return 1;
    if(k==2)return 5;
    Mat t=power(base,k-2);
    LL f=((3*t.a[0][0])%MOD+t.a[1][0])%MOD;
    LL g=(2*(((f-(3*t.a[0][1]+t.a[1][1])-1)%MOD+MOD)%MOD))%MOD;
    return (g+f)%MOD;
}
inline LL calc(){
    LL res=0;
    for(int i(1);i*i<=n;i++)
        if(n%i==0){
            res=(res+mul(get_phi(i),get_T(n/i)))%MOD;
            if((n/i)!=i)res=(res+mul(get_phi(n/i),get_T(i)))%MOD;
        }
    return res/n;
}
int main(){
    freopen("circle.in","r",stdin);
    freopen("circle.out","w",stdout);
    sieve();int ff=0;
    base.a[0][0]=3;base.a[0][1]=1;base.a[1][0]=-1;base.a[1][1]=0;  
    scanf("%d%I64d%d",&n,&MOD,&ff);
    if(ff)printf("%I64d\n",get_T(n));
    else{
        MOD=(LL)n*MOD;
        printf("%I64d\n",calc()%(MOD/n));
    }
    return 0;
}

最后暴力分也没得全QAQ
再也不看见题就瞎写链剖了QAQ

【学长虐学弟欢乐赛系列】Round4

标签:随便搞搞

原文地址:http://blog.csdn.net/creationaugust/article/details/44039589

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!