zoj4097

转移自老blog

zoj4097

题意:
给你一幅图(vert<1e5,edge<2e5),多组询问(<1e5),每次询问有三个点,w,u,v 问你是否存在从w到u和v的边不相交路径。
边双联通缩点,特判图的连通性。
#include<bits/stdc++.h>
using namespace std;

struct Graph{
    static const int maxn=1e5+5, maxm=3e5+5;
    struct star{int v,nex;}edge[maxm<<1];
    int head[maxn],cnt;
    void ini(int n){
        for(int i=0;i<=n;i++) head[i]=-1;
        cnt=-1;
    }
    void add_edge(int u,int v){
        edge[++cnt]=star{v,head[u]};
        head[u]=cnt;
        edge[++cnt]=star{u,head[v]};
        head[v]=cnt;
    }
};

struct Tarjan:Graph{//双联通分量, 割边, 桥, 边双联通缩点
    struct Bridge{int u,v;}bridge[maxn];
    int dfn[maxn],low[maxn],belong[maxn],vis[maxn],sta[maxn],sta_,nums,bridge_;
    void ini(int n){
        for(int i=0;i<=n;i++) vis[i]=0;
        bridge_=0;
        nums=0;
        Graph::ini(n);
    }
    void tarjan(int u,int father,int&step){
        low[u]=dfn[u]=++step;
        sta[++sta_]=u;
        vis[u]=1;
        bool firsttimes=true;//用于判重边
        for(int i=head[u];~i;i=edge[i].nex){
            int v=edge[i].v;
            if(v==father&&firsttimes) {
                firsttimes=false;
                continue;
            }//父边
            if(vis[v]==1) low[u]=min(low[u],dfn[v]);//回边,终点在栈中
            else {//树边
                tarjan(v,u,step);
                low[u]=min(low[u],low[v]);
                if(low[v]>dfn[u]) bridge[++bridge_]=Bridge{u,v};
            }
        }
        if(low[u]==dfn[u]){
            while(sta[sta_]!=u) belong[sta[sta_--]]=nums+1;
            belong[sta[sta_--]]=++nums;
        }
    }
}graph;

struct Lca:Graph{// 不要忘记ini
    int dep[maxn],dad[maxn],siz[maxn],son[maxn],chain[maxn],dfn[maxn];
    void dfs1(int u,int father){//dfs1(1,0)
        dep[u]=dep[father]+1;//ini
        dad[u]=father;
        siz[u]=1;
        son[u]=-1;
        for(int i=head[u];~i;i=edge[i].nex){
            int v=edge[i].v;
            if(v==father)continue;
            dfs1(v,u);
            siz[u]+=siz[v];
            if(son[u]==-1||siz[son[u]]<siz[v]) son[u]=v;
        }
    }
    void dfs2(int u,int s,int&step){
        dfn[u]=++step;
        chain[u]=s;
        if(son[u]!=-1) dfs2(son[u],s,step);
        for(int i=head[u];~i;i=edge[i].nex){
            int v=edge[i].v;
            if(v!=son[u]&&v!=dad[u]) dfs2(v,v,step);
        }
    }
    int lca(int x,int y){
        while(chain[x]!=chain[y]){
            if(dep[chain[x]]<dep[chain[y]]) swap(x,y);//dep[chain[x]]>dep[chain[y]]
            x=dad[chain[x]];
        }
        return dep[x]<dep[y]?x:y;
    }
}tree;

inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}

//Disjoint Set Union
struct Dsu{
    static const int maxn=1e5+5;
    int f[maxn];
    void ini(int n){for(int i=0;i<=n;i++)f[i]=i;}
    int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
    void join(int x,int y){f[find(x)]=find(y);}
}dsu,dsu2;

int main(){
    int T=read();
    while(T--){
        int n=read(),m=read(),q=read();
        graph.ini(n+1);
        dsu.ini(n+1);
        dsu2.ini(n+1);
        for(int i=0;i<m;i++) {
            int u=read(),v=read();
            graph.add_edge(u,v);
            dsu.join(u,v);
            dsu2.join(u,v);
        }
        for(int i=1;i<=n;i++){
            if(dsu2.find(i)!=dsu2.find(n+1)){
                dsu2.join(i,n+1);
                graph.add_edge(i,n+1);
            }
        }
        int step=0;
        graph.tarjan(n+1,0,step);
        tree.ini(graph.nums);
        for(int i=1;i<=graph.bridge_;i++){
            int u=graph.bridge[i].u,v=graph.bridge[i].v;
            tree.add_edge(graph.belong[u],graph.belong[v]);
        }
        tree.dfs1(graph.belong[n+1],0);
        step=0;
        tree.dfs2(graph.belong[n+1],graph.belong[n+1],step);
        for(int i=0;i<q;i++) {
            int w=read(),u=read(),v=read();
            int bw=graph.belong[w];
            int bu=graph.belong[u];
            int bv=graph.belong[v];
            int lcawu=tree.lca(bw,bu);
            int lcawv=tree.lca(bw,bv);
            int lcauv=tree.lca(bu,bv);
            int lcauw=lcawu,lcavw=lcawv,lcavu=lcauv;
            if(dsu.find(w)==dsu.find(u)&&dsu.find(w)==dsu.find(v)){
                if(bw==bu&&bw==bv) puts("Yes");//1
                else if(bu==bv&&bw!=bu) puts("No");//2
                else if(bu==bw&&bu!=bv) puts("Yes");//2
                else if(bv==bw&&bu!=bv) puts("Yes");//2
                else{//3
                    if(lcavw==bw&&lcawu==bu) puts("Yes");
                    else if(lcauw==bw&&lcawv==bv) puts("Yes");
                    else if(lcauw==bw&&tree.dep[lcauv]<=tree.dep[lcawv]) puts("Yes");
                    else if(lcavw==bw&&tree.dep[lcauv]<=tree.dep[lcawu]) puts("Yes");
                    else puts("No");
                }
            }
            else{
                puts("No");
            }
        }
    }
}




/*

1
5 4 1
4 3
1 2
1 4
5 3
1 5 2



*/

2019牛客D

转移自老blog

2019牛客D


简单化简一下要我们求的东西

其实这个就是异或卷积fwt的定义,把前两个求和合成一个求和,用-1的|S|次方构造原数列,跑一次fwt就是答案
#include<bits/stdc++.h>
using namespace std;

int sum[1<<10],cnt[1<<20];
int mod=1e9+7;

int qpow(int a,int b){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%mod;
        a=1ll*a*a%mod;
        b>>=1;
    }
    return ret;
}

//求卷积a[]=>fwt(n,0)=>fwt[]=>fwt(n,1)=>a[]
//fwt(x$y)=fwt(x)*fwt(y);$代表|,&,^
void fwt(int *a, int n, int f) {
    for (int k = 1; k < n; k <<= 1)
        for (int i = 0; i < n; i += (k << 1))
            for (int j = 0; j < k; j++)
                if (f == 1) {
                    int x = a[i + j], y = a[i + j + k];
                    //&:a[i+j]+=a[i+j+k];
                    //|:a[i+j+k]+=a[i+j];
                    a[i + j] = x + y;
                    a[i + j + k] = x - y;
                } else {
                    int x = a[i + j], y = a[i + j + k];
                    //&:a[i+j]-=a[i+j+k];
                    //|:a[i+j+k]-=a[i+j];
                    a[i + j] = (x + y) / 2;
                    a[i + j + k] = (x - y) / 2;
                }
}

int main(){
    ios::sync_with_stdio(false);
    int n,m,k;
    while(cin>>n>>m>>k){
        for(int i=0;i<1<<k;i++) cnt[i]=0;
        for(int i=0;i<n;i++){
            int a[10];
            for(int j=0;j<m;j++) cin>>a[j];
            for(int s=0;s<1<<m;s++){
                if(s!=0) sum[s]=sum[s&(s-1)]^a[__builtin_ffs(s)-1];
                if(__builtin_parity(s)) cnt[sum[s]]--;
                else cnt[sum[s]]++;
            }
        }
        fwt(cnt,1<<k,1);

        int ans=0, rev=qpow(1<<m,mod-2),mul=1;
        for(int i=0;i<1<<k;i++){
            ans^=1ll*mul*cnt[i]%mod*rev%mod;
            mul=3ll*mul%mod;
        }
        cout<<ans<<endl;
    }
}

bzoj-2655

转移自老blog

bzoj2655

题意:
 
一个序列a1,...,an是合法的,当且仅当:  
 长度为给定的n。  
 a1,...,an都是[1,A]中的整数。  
 a1,...,an互不相等。  
 一个序列的值定义为它里面所有数的乘积,即a1a2...an。  
 求所有不同合法序列的值的和。  
 两个序列不同当且仅当他们任意一位不一样。  
 输出答案对一个数mod取余的结果。
// f(i,j)-> 前i个元素中最大值为j的方案的权的和 // f(i,j)=f(i-1,j-1)*i*j+f(i,j-1) // 用数学归纳法证明f(i,j)关于j是一个最高次为2*i的多项式
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
ll mod;

ll qpow(ll a,ll b,ll mod){
    ll ret=1;
    while(b){
        if(b&1) ret=ret*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ret;
}

//  (mod%i)=== -mod/i*i
const int maxn=1200;
ll fac_inv[maxn]={1,1};
void inv_ini(){
    for(ll i=0,fac=1;i<maxn;i++,fac=fac*i%mod) {
        fac_inv[i]=qpow(fac,mod-2,mod);
    }
}

ll prepre[maxn],suf[maxn],*pre=prepre+1;
ll getval(ll *y,ll n,ll x){// O(n) n次多项式有n+1项 y[0]...y[n] -> y[x]
    pre[-1]=suf[n+1]=1;
    for(int i=0;i<=n;i++) pre[i]=pre[i-1]*(x-i+mod)%mod;
    for(int i=n;i>=0;i--) suf[i]=suf[i+1]*(i-x+mod)%mod;
    ll ret=0;
    for(int i=0;i<=n;i++) {
        ll up=pre[i-1]*suf[i+1]%mod;
        ll down=fac_inv[i]*fac_inv[n-i]%mod;
        ret=(ret+y[i]*up%mod*down)%mod;
    }
    return ret;
}

// f(i,j)-> 前i个元素中最大值为j的方案的权的和
// f(i,j)=f(i-1,j-1)*i*j+f(i,j-1)
// 用数学归纳法证明f(i,j)关于j是一个最高次为2*i的多项式
ll f[maxn][maxn*3];
int main(){
    ll n,a;
    while(~scanf("%lld%lld%lld",&a,&n,&mod)){
        inv_ini();
        for(ll j=1;j<=3*n;j++) f[1][j]=(f[1][j-1]+j)%mod;
        for(ll i=2;i<=n;i++){
            for(ll j=i;j<=3*n;j++){
                f[i][j]=(i*j*f[i-1][j-1]+f[i][j-1])%mod;
            }
        }
        //we know f(n) f(n+1) ... f(3n)
        //if g(i)=f(i+n)
        // than f(a)=g(a-n)
        printf("%lld\n",getval(f[n]+n,2*n,(a-n+mod)%mod));
    }
}

cf995f

转移自老blog

cf995f

题意:
 给你最多n=3000个节点的树,让你用1-d(d<1e9)来给树编号,一个节点的号必须小于等于他的所有祖先,问你编号的方案数

// f(i,j)-> i子树中最大值为j的方案数 i<=n j<=d
// f(i,j) = prod(f(son,j))+f(i,j-1) 1<=t<=j
// 用数学归纳法证明f(i,j)关于j是一个最高次为i的多项式
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
ll mod=1e9+7;

ll qpow(ll a,ll b,ll mod){
    ll ret=1;
    while(b){
        if(b&1) ret=ret*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ret;
}

//  (mod%i)=== -mod/i*i
const ll maxn=3005;
ll fac_inv[maxn]={1,1};
void inv_ini(){
    for(ll i=0,fac=1;i<maxn;i++,fac=fac*i%mod) {
        fac_inv[i]=qpow(fac,mod-2,mod);
    }
}

ll prepre[maxn],suf[maxn],*pre=prepre+1;
ll getval(ll *y,ll n,ll x){// O(n) n次多项式有n+1项 y[0]...y[n] -> y[x]
    pre[-1]=suf[n+1]=1;
    for(ll i=0;i<=n;i++) pre[i]=pre[i-1]*(x-i+mod)%mod;
    for(ll i=n;i>=0;i--) suf[i]=suf[i+1]*(i-x+mod)%mod;
    ll ret=0;
    for(ll i=0;i<=n;i++) {
        ll up=pre[i-1]*suf[i+1]%mod;
        ll down=fac_inv[i]*fac_inv[n-i]%mod;
        ret=(ret+y[i]*up%mod*down)%mod;
    }
    return ret;
}

// f(i,j)-> i子树中最大值为j的方案数  i<=n j<=d
// f(i,j) = prod(f(son,j))+f(i,j-1) 1<=t<=j
// 用数学归纳法证明f(i,j)关于j是一个最高次为i的多项式

ll dp[maxn][maxn], f[maxn];
int main(){
    inv_ini();
    ll n,d;
    while(~scanf("%lld%lld",&n,&d)){
        for(ll i=2;i<=n;i++) scanf("%lld",f+i);
        for(ll i=n;i>=1;i--) {
            for(ll j=1;j<=n+1;j++) dp[i][j]=1;
        }

        for(ll i=n;i>=1;i--) {
            for(ll j=2;j<=n+1;j++) {
                dp[i][j]+=dp[i][j-1];
                dp[i][j]%=mod;

                dp[f[i]][j]*=dp[i][j];
                dp[f[i]][j]%=mod;
            }
        }
        // f(1) f(2) .. f(n+1)
        // g(0) g(1) .. g(n) -> g(x)=f(x+1)
        // f(x)=g(x-1)
        printf("%lld\n",getval(dp[1]+1,n,d-1));
    }
}

hdu6428

转移自老blog

bzoj1924

题意:

        在宽广的非洲荒漠中,生活着一群勤劳勇敢的羊驼家族。被族人恭称为“先知”的Alpaca L. Sotomon是这个家族的领袖,外人也称其为“所驼门王”。所驼门王毕生致力于维护家族的安定与和谐,他曾亲自率军粉碎河蟹帝国主义的野蛮侵略,为族人立下赫赫战功。所驼门王一生财宝无数,但因其生性节俭低调,他将财宝埋藏在自己设计的地下宫殿里,这也是今天Henry Curtis故事的起点。Henry是一个爱财如命的贪婪家伙,而又非常聪明,他费尽心机谋划了这次盗窃行动,破解重重机关后来到这座地下宫殿前。
        整座宫殿呈矩阵状,由R×C间矩形宫室组成,其中有N间宫室里埋藏着宝藏,称作藏宝宫室。宫殿里外、相邻宫室间都由坚硬的实体墙阻隔,由一间宫室到达另一间只能通过所驼门王独创的移动方式——传送门。所驼门王为这N间藏宝宫室每间都架设了一扇传送门,没有宝藏的宫室不设传送门,所有的宫室传送门分为三种:
        “横天门”:由该门可以传送到同行的任一宫室;
        “纵寰门”:由该门可以传送到同列的任一宫室;
        “自*河蟹*由*河蟹*门”:由该门可以传送到以该门所在宫室为中心周围8格中任一宫室(如果目标宫室存在的话)。
        深谋远虑的Henry当然事先就搞到了所驼门王当年的宫殿招标册,书册上详细记录了每扇传送门所属宫室及类型。而且,虽然宫殿内外相隔,但他自行准备了一种便携式传送门,可将自己传送到殿内任意一间宫室开始寻宝,并在任意一间宫室结束后传送出宫。整座宫殿只许进出一次,且便携门无法进行宫室之间的传送。不过好在宫室内传送门的使用没有次数限制,每间宫室也可以多次出入。
        现在Henry已经打开了便携门,即将选择一间宫室进入。为得到尽多宝藏,他希望安排一条路线,使走过的不同藏宝宫室尽可能多。请你告诉Henry这条路线最多行经不同藏宝宫室的数目。

输入:
        第一行给出三个正整数 N, R, C。 以下 N 行,每行给出一扇传送门的信息,包含三个正整数xi, yi, Ti,表示该传送门设在位于第 xi行第yi列的藏宝宫室,类型为 Ti。Ti是一个1~3间的整数, 1表示可以传送到第 xi行任意一列的“横天门”,2表示可以传送到任意一行第 yi列的“纵寰门”,3表示可以传送到周围 8格宫室的“自由门”。 保证 1≤xi≤R,1≤yi≤C,所有的传送门位置互不相同。
测试点编号:
        
        N  R  C 
        1  16  20  20 
        2  300  1,000  1,000 
        3  500  100,000  100,000  
        4  2,500  5,000  5,000  
        5  50,000  5,000  5,000  
        6  50,000  1,000,000  1,000,000  
        7  80,000  1,000,000  1,000,000  
        8  100,000  1,000,000  1,000,000  
        9  100,000  1,000,000  1,000,000  
        10  100,000  1,000,000  1,000,000  
先简单化简一下

观察发现

代回去

这些都是积性函数,直接筛就行了
#include<bits/stdc++.h>
using namespace std;

/****  * 超级积性函数线性筛 *  ****/
typedef unsigned int uint;
typedef unsigned long long ull;

const uint maxn=1e7+100;
uint no_pri[maxn]={0,1,0},pri[maxn],low[maxn];
uint PHI[maxn],phu[maxn],f2[maxn],f3[maxn];

void f_ini(){
    for(uint i=2;i<maxn;i++){
        if(!no_pri[i]) low[i]=pri[++pri[0]]=i;
        for(uint j=1;1ll*pri[j]*i<maxn;j++){
            no_pri[pri[j]*i]=1;
            if(i%pri[j]==0) {
                low[pri[j]*i]=low[i]*pri[j];
                break;
            }
            else low[pri[j]*i]=pri[j];
        }
    }

    PHI[1]=phu[1]=f2[1]=f3[1]=1;// 改这里
    for(uint i=1;i<=pri[0];i++){
        for(ull mul=pri[i],ct=1;mul<maxn;mul*=pri[i],ct++){
            uint pre=mul/pri[i];
            PHI[mul]=mul/pri[i]*(pri[i]-1);// 改这里
            phu[mul]=PHI[mul]-PHI[pre];
            f2[mul]=ct%2==1?(f2[pre]*pri[i]):f2[pre];
            f3[mul]=ct%3==1?(f3[pre]*pri[i]):f3[pre];
        }
    }

    for(uint i=2;i<maxn;i++){
        for(uint j=1;1ll*pri[j]*i<maxn;j++){
            uint x=low[i*pri[j]], y=i*pri[j]/x;
            phu[x*y]=phu[x]*phu[y];
            f2[x*y]=f2[x]*f2[y];
            f3[x*y]=f3[x]*f3[y];
            if(i%pri[j]==0) break;
        }
    }
}

int main(){
    f_ini();

    uint t; cin>>t;
    while(t--) {
        uint a,b,c;
        cin>>a>>b>>c;
        uint ans=0;
        uint n=min(min(1.0*a,1.0*b*b),1.0*c*c*c)+0.5;
        for(uint i=1;i<=n;i++){
            ans+=phu[i]*(a/i)*(b/f2[i])*(c/f3[i]);
        }
        ans&=0x3fffffff;
        cout<<ans<<endl;
    }
    return 0;
}





/*


4
96 93 95
970 906 893
92460 95043 54245
9760979 8053227 7156842


*/

hdu6584

转移自老blog

hdu6584

题目本质上是 找分子分母小于n的第k小的真分数,我们直接对答案二分即可,采取分数二分的方式,找到一个区间,它包含最多一个解
$$ \begin{aligned} &答案肯定是找到一个分子分母小于n的分数\frac{p}{q}他满足下面的特征\\ &(\sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)=1][\frac{i}{j} \leq \frac{p}{q}]) = k\\ &我们对左式化简\\ &=\sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)=1][i \leq \frac{p}{q}j]\\ &=\sum_{i=1}^{\lfloor \frac{p}{q}j\rfloor}\sum_{j=1}^n[gcd(i,j)=1]\\ &=\sum_{i=1}^{\lfloor \frac{p}{q}j\rfloor}\sum_{j=1}^ne(gcd(i,j))\\ &=\sum_{i=1}^{\lfloor \frac{p}{q}j\rfloor}\sum_{j=1}^n(u*1)(gcd(i,j))\\ &=\sum_{i=1}^{\lfloor \frac{p}{q}j\rfloor}\sum_{j=1}^n\sum_{d|gcd(i,j)}u(d)*1(d)\\ &=\sum_{i=1}^{\lfloor \frac{p}{q}j\rfloor}\sum_{j=1}^n\sum_{d|gcd(i,j)}u(d)\\ &=\sum_{i=1}^{\lfloor \frac{p}{q}j\rfloor}\sum_{j=1}^n\sum_{d|i,d|j}u(d)\\ &=\sum_{d=1}^{n}u(d)\sum_{i=1}^{\lfloor \frac{p}{q}j\rfloor}\sum_{j=1}^n[d|i,d|j]\\ &=\sum_{d=1}^{n}u(d)\sum_{xd=1}^{\lfloor \frac{p}{q}(yd)\rfloor}\sum_{yd=1}^n[d|(xd),d|(yd)]\\ &=\sum_{d=1}^{n}u(d)\sum_{x=1}^{\lfloor\frac{\lfloor \frac{p}{q}(yd)\rfloor}{d}\rfloor}\sum_{y=1}^{\lfloor\frac{n}{d}\rfloor}1\\ &=\sum_{d=1}^{n}u(d)\sum_{y=1}^{\lfloor\frac{n}{d}\rfloor}\lfloor\frac{\lfloor \frac{p}{q}(yd)\rfloor}{d}\rfloor\\ &=\sum_{d=1}^{n}u(d)\sum_{y=1}^{\lfloor\frac{n}{d}\rfloor}{\lfloor \frac{p}{q}y\rfloor}\\ \end{aligned} $$
这里是可以求出答案的,对d分块,右边的部分采用类欧几里得算法
我们一直往下二分,直到区间足够小,最后用 Stern-Brocot Tree 或 法雷序列找出答案
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

/****  * 超级积性函数线性筛 *  ****/
typedef long long ll;
const ll maxn=2e6+10;
ll no_pri[maxn]={0,1,0},pri[maxn],low[maxn];
ll PHI[maxn],DDD[maxn],XDX[maxn],MUU[maxn],SIG[maxn];
void f_ini(){
    for(ll i=2;i<maxn;i++){
        if(!no_pri[i]) low[i]=pri[++pri[0]]=i;
        for(ll j=1;pri[j]*i<maxn;j++){
            no_pri[pri[j]*i]=1;
            if(i%pri[j]==0) {
                low[pri[j]*i]=low[i]*pri[j];
                break;
            }
            else low[pri[j]*i]=pri[j];
        }
    }

    DDD[1]=PHI[1]=MUU[1]=SIG[1]=1;// 改这里
    for(ll i=1;i<=pri[0];i++){
        for(ll mul=pri[i],ct=1;mul<maxn;mul*=pri[i],ct++){
            DDD[mul]=ct+1;// 改这里
            SIG[mul]=SIG[mul/pri[i]]+mul;// 改这里
            MUU[mul]=ct==1?-1:0;// 改这里
            PHI[mul]=mul/pri[i]*(pri[i]-1);// 改这里
        }
    }

    for(ll i=2;i<maxn;i++){
        for(ll j=1;pri[j]*i<maxn;j++){
            ll x=low[i*pri[j]], y=i*pri[j]/x;
            DDD[x*y]=DDD[x]*DDD[y];
            MUU[x*y]=MUU[x]*MUU[y];
            PHI[x*y]=PHI[x]*PHI[y];
            SIG[x*y]=SIG[x]*SIG[y];
            if(i%pri[j]==0) break;
        }
    }

    for(ll i=1;i<maxn;i++) {
        DDD[i]+=DDD[i-1];
        MUU[i]+=MUU[i-1];
        PHI[i]+=PHI[i-1];
        SIG[i]+=SIG[i-1];
        XDX[i]=(DDD[i]-DDD[i-1])*i+XDX[i-1];
    }
}


struct frac{
    ll x,y;
    frac(ll x_=0,ll y_=1){
        ll gcd=__gcd(x_,y_);
        x=x_/gcd;
        y=y_/gcd;
    }
    frac operator +(const frac&rhs){
        ll lcm=y/__gcd(y,rhs.y)*rhs.y;
        return frac(x*(lcm/y)+rhs.x*(lcm/rhs.y),lcm);
    }
    frac operator /(ll k){
        ll gcd=__gcd(k,x);
        return frac(x/gcd,y*(k/gcd));
    }
    bool operator <=(const frac&rhs){
        ll lcm=y/__gcd(y,rhs.y)*rhs.y;
        return x*(lcm/y)<=rhs.x*(lcm/rhs.y);
    }
};

// a>=0 b>=0 c>0 n>=0         -> O(lg(a,c))
void calfgh(ll a,ll b,ll c,ll n,ll&f,ll&g,ll&h){
    ll A=a/c,B=b/c,s0=n+1,s1=n*(n+1)/2,s2=n*(n+1)*(2*n+1)/6;
    f=s1*A+s0*B;
    g=s2*A+s1*B;
    h=s2*A*A+s0*B*B+2*s1*A*B-2*B*f-2*A*g;// 先减掉
    a%=c,b%=c;
    ll m=(a*n+b)/c;
    if(m!=0) {
        ll ff,gg,hh; calfgh(c,c-b-1,a,m-1,ff,gg,hh);
        f+=n*m-ff;
        g+=(n*m*(n+1)-hh-ff)/2;
        h+=n*m*m-2*gg-ff;
    }
    h+=2*B*f+2*A*g;//再加上
}


ll count(frac k,int n){
    ll ret=0;
    for(int i=1,ed;i<=n;i=ed+1){
        ed=n/(n/i);
        ll a[3]; calfgh(k.x,0,k.y,n/i,a[0],a[1],a[2]);
        ret+=1ll*(MUU[ed]-MUU[i-1])*a[0];
    }
    return ret;
}

int main(){
    f_ini();
    ll t,n,k;
    scanf("%lld",&t);
    while(t--){
        scanf("%lld%lld",&n,&k);
        frac l(0,1),r(1,1);// [l,r]
        for(int ijk=0;ijk<40;ijk++){
            frac mid=(l+r)/2;
            ll ct=count(mid,n);//[0,mid]
            if(ct>=k)r=mid;
            else l=mid;
        }
        //[l,r]
        frac L(0,1),R(1,0);
        while(true){
            frac mid(L.x+R.x,L.y+R.y);
            if(mid.x<=n&&mid.y<=n&&l<=mid&&mid<=r){
                printf("%lld/%lld\n",mid.x,mid.y);
                break;
            }
            if(!(l<=mid)){
                L=mid;
            }
            if(!(mid<=r)){
                R=mid;
            }
        }
    }
}

ACM-ICPC北京赛区2018-D-Frog and Portal

转移自老blog

ACM-ICPC北京赛区2018-D-Frog and Portal

题目大意 :
       有一只青蛙站在位置0,他可以选择往前跳一步或者两步,他希望跳到位置200上,他有多少种跳法呢? 这其实是个斐波拉契数,如果我们加强难度,假设每个位置上可以创建最多一个传送门,可以传送到任何地方,传送的规则是传送到不能传送为止 比方说,位置1上有个传送门,传送到2,2上有门传送到3,3又传送到4,4上没有传送门,于是当青蛙跑到1上的时候,他会被立刻传送到4上,当然他可以 选择从0跳过1,一次性跳到2,他就不会进入1的传送门,很可惜,2上面的传送门将它最终送到了4 ,当然如果传送门构成死循环,青蛙就永远无法出来了。
       现在要你构造传送门,使得青蛙有恰好k种办法跳到位置200,k在int内         
       现首先考虑到两段连续的不含有传送门的位置,中间若只隔着一个i到i到传送门,那么到终点到方案数 一定是几个斐波拉契数的积,很遗憾,斐波拉契数不是质数,它不具有像质数集合可以用几个元素的积来构造整数集合一样的性质,这个办法失效
       然后我们仔细思考,发现如果位置i的传送门传送到i-1,i-2,i-3。。。这些数的话,方案数是无穷大,
       第一段表明只同i->i的传送门不可能完成,第二段表示不能使用或者说不使用传送门i->i-k(k>0)比使用 更加优,于是我们得到了一个简单的推论:答案可以由i->i 与i->i+k两者结合得到
       紧接着我们开始分析前几个格子的跳跃方案数:
方案 1 1 2 3 5
位置 0 1 2 3 4
       考虑在位置3放一个传送门传送到i:
方案 1 1 2 3 2 2 4
位置 0 1 2 3 4 5 6
       这个时候我们应该警觉到了,方案数出现了4,出现了4,4不是斐波拉契数。再继续观察,位置3到底怎么回事?他对谁有贡献?
       如果我们假设让位置3跳到自己,他会对答案贡献0,如果我们让位置3跳到200,他会对答案贡献3
       选或不选+1 2 4的出现,让我们联想到了利用二进制,
       然后我们构建了一个周期为6的自动匹配的东西,
方案 1 1 1 1 2 3 ...
位置 0 1 2 3 4 5 ...
       绿色代表i->i死循环,红色部分可选死循环或者跳到终点,表示选或不选,6*32=192,我们在最后的地方连续弄两个 i->i就可以保证到达终点的一定是周期内部的方案。于是范围跨度194,传送门稳定66个,就可以根据输入的k的二进制 表示来调整红色部分的传送门即可。于是题目被解决了。
#include <bits/stdc++.h>
using namespace std;

void choose(int i){
    printf("%d %d\n",i+1,199);
    printf("%d %d\n",i+5,i+5);
}


void no_choose(int i){
    printf("%d %d\n",i+1,i+1);
    printf("%d %d\n",i+5,i+5);
}

int main(){
    int n;
    while(~scanf("%d",&n)){
        printf("66\n");
        for(int i=0;i<32;i++){
            if(n&(1<<i)){
                choose(6*i);
            }
            else {
                no_choose(6*i);
            }
        }
        printf("197 197\n");
        printf("198 198\n");
    }
}

dfs_enumeration_partition

转移自老blog

CCPC-Wannafly Winter Camp Day2 (Div1, online mirror) Sticks

题目大意 :
        给你12根棍子,让你对棍子分四堆,每堆三根, 要求这四堆棍子尽可能组成多的三角形
         解法1:考虑到划分只有3万个左右,实际上dfs枚举划分就可以ac。详见代码
         解法2:考虑分块,先分成6+6,有接近1000种分法,然后6=3+3,6=3+3,但是我们有必要用一边的6=3+3 的所有划分去匹配另一边的6=3+3的划分吗?其实不需要,我们只需要单独考虑两边的6=3+3,取最优分法 然后组合在一起,因为互不影响,所以可以直接最优对最优,省掉了很大一部分的讨论,复杂度为12=6+6的 划分的种类数乘以2再乘以6=3+3的划分的种类数大概为2万,相比解法1优化了一部分常数。
        我使用解法2的代码很蠢很长,就不拿出来了。
#include<bits/stdc++.h>
using namespace std;

int dfs(int*a,int n){
    if(n==3)return a[0]+a[1]>a[2];
    
    int ret=0,ret_array[12]{};
    int b[12],b_=0;
    
    for(int i=1;i<n;i++){
        for(int j=i+1;j<n;j++){
            b_=0;
            for(int k=1;k<n;k++){
                if(k==i||k==j)continue;
                b[b_++]=a[k];
            }
            int tmp=dfs(b,n-3);
            if(tmp+(a[0]+a[i]>a[j])>ret){
                ret=tmp+(a[0]+a[i]>a[j]);
                memcpy(ret_array,b,sizeof(int)*(n-3));
                ret_array[n-3]=a[0];
                ret_array[n-2]=a[i];
                ret_array[n-1]=a[j];
            }
        }
    }
    memcpy(a,ret_array,sizeof(int)*n);
    return ret;
}

int main(){
    int a[12],T;
    scanf("%d",&T);
    
    for(int times=1;times<=T;times++){
        for(int i=0;i<12;i++)scanf("%d",a+i);
        sort(a,a+12);
        int ans=dfs(a,12);
        printf("Case #%d: %d\n",times,ans);
        for(int i=0;i<12;i+=3){
            if(a[i]+a[i+1]>a[i+2]){
                printf("%d %d %d\n",a[i],a[i+1],a[i+2]);
            }
        }
    }
}

hdu5738

转移自老blog

hdu5738

题目核心 :对二维平面的点按照直线计数,等价于计算出每一条直线上的点的个数
        
枚举起点,再枚举终点,对斜率用map计数,可以优化常数,不要用double,会丢失精度,考虑到斜率不参与加减运算,可以用分数形式储存。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pll;

const int MOD=1e9+7;
ll qpow2[2000];

void initpow(){
    qpow2[0]=1;
    for(int i=1;i<2000;i++){
        qpow2[i]=2*qpow2[i-1]%MOD;
    }
}

map<pll,int>mp;

ll x[2000],y[2000];


ll __gcd(ll a,ll b){
    return a==0?b:__gcd(b%a,a);
}

int main(){
    initpow();
    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        scanf("%d",&n);
        
        for(int i=0;i<n;i++){
            scanf("%lld%lld",x+i,y+i);
        }
        
        ll ans=0;
        for(int i=0;i<n;i++){
            mp.clear();
            ll tem=0;
            for(int j=i+1;j<n;j++){
                if(x[i]==x[j]&&y[i]==y[j]){
                    tem++;
                }
                else{
                    ll gcd=__gcd(y[i]-y[j],x[i]-x[j]);
                    mp[pll((y[i]-y[j])/gcd,(x[i]-x[j])/gcd)]++;
                }
            }
            
            ans+=qpow2[tem]-1+MOD;
            ans%=MOD;
            
            for(auto x:mp){
                ans+=(qpow2[x.second]-1+MOD)*qpow2[tem];
                ans%=MOD;
            }
        }
        
        printf("%lld\n",ans);
    }
}

01树

转移自老blog

01树

/*******01字典树部分********/
int ch[maxn*100][2];int tot=0;int root[maxn];
//新建N个字典树

//向根为u的字典树插入x
void insert(int u,int x){
    for(int i=18;i>=0;i--){
        int id=(x>>i)&1;
        if(!ch[u][id])
            ch[u][id]=++tot;
        u=ch[u][id];
    }
}
//查询根为u的字典树与x的异或最大值
int query(int u,int x){
    int res=0;
    for(int i=18;i>=0;i--){
        int id=(x>>i)&1;
        if(ch[u][id^1]){
            u=ch[u][id^1];
            res|=(1<<i);
        }
        else u=ch[u][id];
    }
    return res;
}
/*****01字典树部分结束**********/