zoukankan      html  css  js  c++  java
  • 10-8 王小呆的校内互坑赛题解

    money

    可以发现一条边被允许走无数次,再经过分析可以知道,对于一个环上的边,可以一直走走走,直到将环上所有的边所有的钱全部得到。所以我们可以先找环,这个过程用Tarjan实现,找到所有的环之后,将环缩成一个点,这个时候需要将环上所有的边的边权加到缩点之后的点上。(注意,这里的边权指的是通过恢复系数累加直至钱数为零时的总和)。之后就会发现我们得到的是一个无环的有向图,那就在跑一遍带点权的最长路即可。(数据并没有卡SPFA,因为出题人不会卡。。。)所以可以放心的跑最短路,又因为题目并没有给出明确的终点,所以需要O(n)枚举每个点的答案,取最大值。

    说几个需要注意的问题吧,这道题的思路其实很简单,不过出题人认为代码是比较复杂的,在代码实现过程中,我们需要先Tarjan缩点找出所有的环,在这个过程中只需要标记每个点属于哪一个强连通分量即可。接着,需要再去枚举每一个点的出边,判断每条边两端的点是否属于同一个强连通分量。如果不是,就连一条新的边,边权等于原来这条边的边权。(就等同于将所有的强连通分量之间连边)。在做完以上一系列工作之后,再跑一遍带点权的最长路,std用的是SPFA,不过堆优化DijA掉这道题也是没有问题的。

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<queue>
    using namespace std;
    inline int read(){
        int sum=0,f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9'){
            if(ch=='-')f=-1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            sum=(sum<<1)+(sum<<3)+ch-'0';
            ch=getchar();
        }
        return sum*f;
    }
    const int wx=300017;
    int num,Num,n,m,s,x,y,z,tot,top,col,ans;
    double c;
    int head[wx],Head[wx],vis[wx],a[wx],dfn[wx],low[wx],size[wx],belong[wx],st[wx],dis[wx];
    struct e{
        int nxt,to,dis,Dis;
        double c;
    }edge[wx*2];
    void add(int from,int to,int dis,double c){
        edge[++num].nxt=head[from];
        edge[num].to=to;
        edge[num].dis=dis;
        edge[num].Dis=dis;
        edge[num].c=c;
        head[from]=num;
    }
    struct E{
        int nxt,to,dis;
    }Edge[wx*2];
    void Add(int from,int to,int dis){
        Edge[++Num].nxt=Head[from];
        Edge[Num].to=to;
        Edge[Num].dis=dis;
        Head[from]=Num;
    }
    void Tarjan(int u){
        dfn[u]=low[u]=++tot;
        st[++top]=u;
        for(int i=head[u];i;i=edge[i].nxt){
            int v=edge[i].to;
            if(!dfn[v]){
                Tarjan(v);
                low[u]=min(low[u],low[v]);
            }
            else if(!belong[v])low[u]=min(low[u],dfn[v]);
        }
        if(dfn[u]==low[u]){
            belong[u]=++col;
            size[col]++;
            while(st[top]!=u){
                belong[st[top]]=col;
                size[col]++;
                top--;
            }
            top--;
        }
    }
    void XJB(){
        for(int u=1;u<=n;u++){
                for(int i=head[u];i;i=edge[i].nxt){
                    int v=edge[i].to;
                    if(belong[v]==belong[u]){
                        int tmp=edge[i].Dis;
                        while(tmp){
                            tmp=(int)tmp*edge[i].c;
                            edge[i].dis+=tmp;
                        }
                    }
                }
        }
    }
    void WTM(){
        for(int u=1;u<=n;u++){
            for(int i=head[u];i;i=edge[i].nxt){
                int v=edge[i].to;
                if(belong[v]==belong[u]){
                    a[belong[u]]+=edge[i].dis;
                }
            }
        }
        for(int u=1;u<=n;u++){
            for(int i=head[u];i;i=edge[i].nxt){
                int v=edge[i].to;
                if(belong[u]!=belong[v]){
                    Add(belong[u],belong[v],edge[i].dis);
                }
            }
        }
    }
    queue<int > q;
    void SPFA(int s){
        for(int i=1;i<=col;i++)dis[i]=0,vis[i]=0;
        vis[s]=1;q.push(s);dis[s]=a[s];
        while(q.size()){
            int u=q.front();q.pop();
            vis[u]=0;
            for(int i=Head[u];i;i=Edge[i].nxt){
                int v=Edge[i].to;
                if(dis[v]<dis[u]+Edge[i].dis+a[v]){
                    dis[v]=dis[u]+Edge[i].dis+a[v];
                    if(!vis[v]){
                        vis[v]=1;
                        q.push(v);
                    }
                }
            }
        }
    }
    void get_ans(){
        for(int i=1;i<=col;i++){
            ans=max(ans,dis[i]);
        }
        printf("%d
    ",ans);
    }
    int main(){
    //	freopen("money004.in","r",stdin);
    //	freopen("money004.out","w",stdout);
        n=read();m=read();
        for(int i=1;i<=m;i++){
            x=read();y=read();z=read();scanf("%lf",&c);
            add(x,y,z,c);
        }
        s=read(); 
        for(int i=1;i<=n;i++){
            if(!dfn[i])Tarjan(i);
        }
        XJB();
        WTM();
        SPFA(belong[s]);
        get_ans();
        return 0;
    } 
    
    

    dream

    发现这是一道数学题。

    具体来说就是求满足$ a^x equiv 1(mod n)$的最小x值加上1

    那么怎么求呢?

    知不知道欧拉定理?其实这就是一道简单的欧拉定理板子题。

    设gcd(a,m)=1,必有正整数x,使得a^x=1(mod m),且设满足等式的最小正整数为x0,必满足x0|phi(m).注意m>1.

    否则如果gcd(a,m)!=1,则方程a^x=1(mod m)没有解。

    欧拉定理:(a^{(φ(n))}equiv 1(mod n))

    证明过程:

    ​ 所有小于等于n并且与n互质的数按大小顺序排布,我们可以设为:(x_1,x_2……x_{φ(n)})。显然,一共有φ(n)个数。

    ​ 接下来我们考虑这么一些数:

    ​ 设(m_1=a*x_1,m_2=a*x_2……m_{φ(n)}=a*x_{φ(n)})

    ​ 可以得出以下几个结论:

    (1.)这些数中的任意两个都不模n同余,因为如果存在(m_{one}equiv m_{two}(mod n)),就会有:(m_{one}-m_{two}=a*(x_{one}-x_{two})=k*n),即n能整除(a*(x_{one}-x_{two}))。但是a和n互质,即a与n的最大公约数为1,然而(x_{one}-x_{two})小于n,因而等式左边不可能被n整除。也就是说:这些数中任意两个都不会模n同余,即φ(n)个数关于n会有φ(n)个余数。

    (2.)这些数关于n的余数都与n互质,因为如果存在:余数与n有公因子r,那么(a*x_i=p*n+q*r=r*(……)),说明a*x_i与n不互质(即存在公约数r不为1),但显然,这是不可能的,那么这些数关于n的余数,全部都在(x_1,x_2……x_{φ(n)})中,因为这些数代表的即是所有小于等于n的与n互质的数。

    ​ 那么,由(1)(2)可知 ,数(m_1,m_2……m_{φ(n)})必须相应地对应地关于n同余于(x_1,x_2……x_{φ(n)})

    ​ 所以得出:(m_1*m_2*m_3*……*m_{φ(n)} equiv x_1*x_2*x_3……x_{φ(n)}(mod n))

    ​ 换一种表达方式可以是:(a^{[φ(n)]}*(x_1*x_2*……*x_{φ(n)})equiv (x_1*x_2*……*x_{φ(n)}))

    ​ 为了更加方便,我们设K为((x_1*x_2*……*x_{φ(n)})),就又可以表示为:(K*(a^{φ(n)}-1)equiv0(mod n))

    ​ 这些都是成立的。

    ​ 所以可得(K*(a^{φ(n)}-1))被n整除,但是K中的因子(x_1,x_2……x_{φ(n)})都与n互质,那么K也与n互质,所以((a^{φ(n)}-1))必须能被n整除,即又可得((a^{φ(n)}-1)equiv0(mod n))成立,即得(a^{φ(n)}equiv1(mod n)),得证。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int wx=1000017;
    int isprime[wx],prime[wx],phi[wx];
    int a,n,tot,t;
    int gcd(int a,int b){
    	return !b?a:gcd(b,a%b);
    }
    int euler_phi(int n){
    	int m = floor(sqrt(n+0.5));
    	int ans = n;
    	for(int i = 2; i <= m; i++)
    	if(n%i == 0){
    	    ans = ans / i * (i-1);        
    		while(n%i == 0){
    		n /= i; }   
    		}    
    	if(n > 1) ans = ans / n *(n-1);    
    	return ans;
    }
    int ksm(int a,int b,int mod){
    	int re=1;
    	while(b){
    		if(b&1)re=re*a%mod;
    		a=a*a%mod;
    		b>>=1;
    	}
    	return re;
    }
    int Fastgcd(int a, int b){
        if (a == 0) return b;
    	if (b == 0) return a;
    	if (!(a & 1) && !(b & 1))      
    	return Fastgcd(a>>1, b>>1)<<1;    
    	else if (!(b & 1))    
    	return Fastgcd(a, b>>1);    
    	else if (!(a & 1)) return Fastgcd(a>>1, b);    
    	else return Fastgcd(abs(a - b), min(a, b));
    }
    signed main(){
    //	freopen("dream.in","r",stdin);
    //	freopen("dream.out","w",stdout);
    	scanf("%d",&t);
    	while(t--){
    		scanf("%d%d",&a,&n);
    		if(Fastgcd(a,n)!=1){
    			printf("w_x_c_q is living in a dream
    ");
    		}
    		else{
    			int tmp=euler_phi(n);
    			int ans=9999999999999LL;
    			for(int i=1;i*i<=tmp;i++){
    				if(tmp%i)continue;
    				if(ksm(a,i,n)==1)ans=min(ans,i);
    				if(ksm(a,tmp/i,n)==1)ans=min(ans,tmp/i);
    			}
    			printf("%d
    ",ans);
    		}
    	}
    	return 0;
    }
    

    hope

    二维偏序简单题。

    将A排序,把A+B作为B。

    保证A有序,将B放到树状数组,每次查询就行了。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    inline int read(){
    	int sum=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){sum=(sum<<1)+(sum<<3)+ch-'0';ch=getchar();}
    	return sum*f;
    }
    const int wx=200177;
    int sum[wx];
    int n,ans;
    struct node{
    	int a,b;
    	friend bool operator < (const node& a,const node & b){
    		return a.a<b.a;
    	}
    }t[wx];
    void add(int pos,long long k){
    	for(int i=pos;i<=n*2;i+=(i&-i)){
    		sum[i]+=k;
    	}
    }
    int query(int x){
    	long long re=0;
    	for(int i=x;i>=1;i-=(i&-i)){
    		re+=sum[i];
    	}
    	return re;
    }
    int main(){
    	freopen("hope001.in","r",stdin);
    	freopen("hope001.out","w",stdout);
    	n=read();
    	for(int i=1,x;i<=n;i++){
    		t[i].a=read();x=read();
    		t[i].b=t[i].a+x;
    	}
    	sort(t+1,t+1+n);
    	for(int i=1;i<=n;i++){
    		ans+=(query(t[i].b-1)-query(t[i].a-1)); 
    		add(t[i].b,1);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    可视化工具D3.js教程 入门 (第十三章)—— 树状图
    可视化工具D3.js教程 入门 (第十二章)—— 力导向图
    可视化工具D3.js教程 入门 (第十一章)—— 饼图
    可视化工具D3.js教程 入门 (第十章)—— 交互式操作
    vue滑动页面选中标题,选中标题滚动到指定区域
    Vue样式穿透
    操作系统:进程和线程+进程的通讯方式
    客户端与服务端长连接的几种方式
    前端性能优化的 24 条建议(2020)-收藏
    idea中修改git提交代码的用户名
  • 原文地址:https://www.cnblogs.com/wangxiaodai/p/9756307.html
Copyright © 2011-2022 走看看