• 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,否则直接暴力??

  • 相关阅读:
    nvidia tx1使用记录--基本环境搭建
    STL hashtable阅读记录
    Linux strace命令
    转一篇:Reactor模式
    C++ 模板特化以及Typelist的相关理解
    C++ 内联函数inline
    迭代器失效的几种情况总结
    C++ Class与Struct的区别
    C++中string.find()函数,string.find_first_of函数与string::npos
    C/C++ 中长度为0的数组
  • 原文地址:https://www.cnblogs.com/zhuier-xquan/p/11211111.html
Copyright © 2020-2023  润新知