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\n",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\n");
}
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\n",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\n",ans);
return 0;
}