zoukankan      html  css  js  c++  java
  • 7.19 讲题

    消防局的设立;luogu2279

    定根之后,找到这棵树中深度最深的叶子节点:

    1.自己 2.兄弟 3.父亲 4.爷爷

    应该选择哪一种?

    显然是4,因为把士兵放在1 2 3位置能覆盖的所有节点,放在4都可以被覆盖;

    找出深度最深的节点,找到他的爷爷,在爷爷的位置放一个士兵,把它爷爷能覆盖到的所有节点直接从树中删掉;

    重复直到没有节点;

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    
    using namespace std;
    
    inline int read(){
        int ans=0;
        char last=' ',ch=getchar();
        while(ch>'9'||ch<'0') last=ch,ch=getchar();
        while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
        if(last=='-') ans=-ans;
        return ans;
    }
    
    int n,cnt,ans;
    int head[1001],deep[1001],nn[1001];
    struct Edge{
        int to,nxt;
    }e[2001];
    
    void add(int from,int to){
        ++cnt;
        e[cnt].to=to;
        e[cnt].nxt=head[from];
        head[from]=cnt;
    }
    bool vis[1001];
    int fa[1001];
    
    bool cmp(int a,int b){return deep[a]>deep[b];}
    
    void dfs(int k){
        for(int i=head[k];i;i=e[i].nxt){
            int v=e[i].to;
            if(!v) continue;
            if(!vis[v]){
                deep[v]=deep[k]+1;
                fa[v]=k;
                vis[v]=1;
                dfs(v);
            }
        }
        return;
    }
    
    void Dfs(int k,int step){
        if(step>2)return ;
        deep[k]=0;
        for(int i=head[k];i;i=e[i].nxt){
            int v=e[i].to;
            if(!vis[v]) {
                vis[v]=1;
                Dfs(v,step+1);
            }
        }
    
    }
    
    void solve(){
        deep[1]=1;vis[1]=1;vis[0]=1;
        dfs(1);
        sort(nn+1,nn+n+1,cmp);
       
        for(int i=1;i<=n;i++){
            if(!deep[nn[i]]) continue;
            int u=fa[fa[nn[i]]]; 
            memset(vis,0,sizeof(vis));    
            vis[0]=1;
            ans++;
            vis[u]=1;
            Dfs(u,0);
        }
    }
    
    int main(){
        n=read();
        int a;
        for(int i=1;i<n;i++){
            a=read();nn[i]=i;
            add(i+1,a);add(a,i+1);
        }nn[n]=n;
        solve();
        printf("%d",ans);
        return 0;
    }
    View Code

    BZOJ 2313

    实际上算的是M分组方案

    step1:求出有多少种分组方案

    求因子个数 for i=1~√n   =>有k个因子

    step2:求有多少种颜色数

    计算M^k =>quick_pow

    up:

    不考虑人在分组中的顺序,但要考虑不同人分配在同一组产生的差异:(有点乱,举个例子:假设6个人,编号为1 2 3 4 5 6,那么分组的话:(123)(456)与(124)(356)是不同的,而(312)(564)与(123)(456)是相同的)

    只要组内的人不完全相同,就认为是不同的两种方案;

    第一组的方案数:从n个人选n/r个人出来Cnn/r

    第二组的方案数:从剩下的n-n/r个人中选出n/r个人Cn-n/rn/r

    ……

    第n组的方案数:从剩下n/r个人中选出n/r个人

    最后因为不考虑排列方式,因此我们还要再除r!

    problem 1:论如何求上面的公式

    problem 2:k(也就是上述式子)应该去mod多少;

    solve problem 1:、

    化简上述式子:

     solve problem 2:

    运用欧拉定理:

    因此k%φ(1e9-401);

    problem 3: n这么大,阶乘该怎么算

    solve:1e9-401是一个质数,

    那么φ(1e9-401)=1e9-402=2*13*5281*7283;

    转化为n!%2,n!%13,n!%5281,n!%7283;

    然后用中国剩余定理??合并方程;

    模的数非常小,意味着一定有循环节

    开始懵逼?????

    求N!%13:

     

    先要知道什么样的(x,y)是先手必胜的:

    ①把黑色格子到顶端的距离缩小

    ②把黑色格子到左端的距离缩小

    ③把黑色格子到低端的距离缩小

    ④把黑色格子到右端的距离缩小

    可以看做有四堆石子的nim石子游戏,

    四堆石子分别是:

    ①x-1个石子

    ②y-1个石子

    ③n-x个石子

    ④m-y个石子

    显然先手必胜或必败,只需要异或一下:

    对于1<=x<=n,1<=y<=m。

    使得(x-1)^(y-1)^(n-x)^(m-y)=0;

    优化枚举过程:

    问有多少个x,y,满足(x-1)^(n-x)=(y-1)^(m-y)(异或相同为0不同为1,若两边相等,异或以后为0)

    首先枚举x,求出所有可能值,放到一个数组里,然后每计算一个y,看在数组中出现了几次。排序+二分的思想(或者使用桶排思想),然后就好了。

    CF 45GPrime Problem

    代码最长的是判断一个数是不是质数;

    哥德巴赫猜想:

    任何一个大于等于4的偶数,可以被拆成两个质数之和;未证明

    任何一个大于等于7的奇数,可以被拆成三个质数之和;已证明

    S=n*(n+1)/2

    if (S%2==0) printf("2");

    if(S%2==1) S是一个质数当且仅当n=2;

    当n=2时 输出1;

    如果S-2是质数 输出2

    否则 输出3

    啊啊啊啊啊啊啊啊啊啊啊啊太艰难了

    以下是CODE:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #define ll long long
    
    using namespace std;
    
    ll n,sum;
    ll ans[10010];
    bool notp[18003001];
    
    void prime(ll sum){
        notp[1]=1;notp[0]=1;
        for(ll i=2;i<=sum;i++){
            if(notp[i]) continue;
            for(ll j=i*i;j<=sum;j+=i)
                notp[j]=1;
        }
    }
    
    void print(){
        for(ll i=1;i<=n;i++)
            cout<<ans[i]<<" ";
        return;
    }
    
    int main(){
        cin>>n;
        for(ll i=1;i<=n;i++) ans[i]=1;
        sum=n*(n+1)/2;
    
        prime(sum);
        if(!notp[sum]) {
            print();
            return 0;
        }
        ll u=-1;
        for(ll i=2;i<sum-1;i++)
            if((!notp[i])&&(!notp[sum-i]))
                u=i;
        
        if(u==-1){
            ans[3]=3;sum-=3;
            for(ll i=2;i<sum-1;i++)
                if((!notp[i])&&(!notp[sum-i]))
                    u=i;
        } 
        
        for(ll i=n;i>=1;i--){
            if(i<=u&&ans[i]==1){
                u-=i;
                ans[i]=2;
            }
        }
        print();
        return 0;
    }
    View Code

    显然对于任意两头牛的曼哈顿距离都是|x1-x2|+|y1-y2|

    我们不妨假设x1>=x2;

    那么就变成了x1-x2+|y1-y2|;

    两种情况:

    ①:y1>y2:x1-x2+y1-y2=(x1+y1)-(x2+y2)

    ②:y1<y2:x1-x2+y2-y1=(x1-y1)-(x2-y2);

    按照x+y排一遍序,按照x-y排一遍序,分别取两次的最大-最小,然后再将这两次最大-最小取max;

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #define ll long long
    
    using namespace std;
    
    int n,x,y;
    int a,b,c,d;
    
    int main(){
        scanf("%d",&n);
        a=-0x7fffffff;
        b=0x7fffffff;
        c=-0x7fffffff;
        d=0x7fffffff;
        for(int i=1;i<=n;i++){
            scanf("%d%d",&x,&y);
            a=max(x+y,a);
            b=min(b,x+y);
            c=max(x-y,c);
            d=min(x-y,d);
        }
        printf("%d",max(a-b,c-d));
        return 0;
    }
    View Code

    先只解决一次询问:

    M[a][b]表示从a城市到b城市总共的路径条数;

    f[i][j]表示走i步走到j的方案数;

    那么:

    最终答案是f[d][v];

    但是非常不幸,复杂度O(n2d),你爆炸了;

    优化?

    我们把M看做一个n*n的矩阵,然后f[i]看做一个1*n的矩阵,那么f[i]=f[i-1]*M;

    运用矩阵快速幂:

    现在复杂的是O(N3logd),还是爆炸了;

    考虑继续优化;

    看k的数据范围,非常扎眼了也是

     考虑是不是可以把复杂度优化成与k有关的:

     考虑到交换顺序并不会影响计算,因此转化为:

    定睛一看:

    矩阵乘法

    然后我们也可以把out和in看成两个矩阵了

    M=out*in;

    然后我们的复杂度就只有O(k3logd)了

    给你一颗n个点的树点权:a1,a2,a3,a4……an < int

    M次询问:给出两点p1,p2,

    能不能在p1,p2的路径上找出三个点,使三个点的点权使之组成一个三角形;

    n,M<=1e6;

     写下点权,然后从小到大排序,不能组成三角形的情况:a3>=a1+a2,a4>=a2+a3,……an>=an-2+an-1;

    如果恰好取等,刚好为斐波那契数列(不能组成三角形的最小(极限)情况),然后因为所有数都不爆int,所以当序列长度>斐波那契数列爆int项的项数时,显然是yes,否则直接暴力??

  • 相关阅读:
    hdu 1290 献给杭电五十周年校庆的礼物 (DP)
    hdu 3123 GCC (数学)
    hdu 1207 汉诺塔II (DP)
    hdu 1267 下沙的沙子有几粒? (DP)
    hdu 1249 三角形 (DP)
    hdu 2132 An easy problem (递推)
    hdu 2139 Calculate the formula (递推)
    hdu 1284 钱币兑换问题 (DP)
    hdu 4151 The Special Number (DP)
    hdu 1143 Tri Tiling (DP)
  • 原文地址:https://www.cnblogs.com/zhuier-xquan/p/11211111.html
Copyright © 2011-2022 走看看