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;
}
原文地址:https://www.cnblogs.com/wangxiaodai/p/9756307.html