1. 程式人生 > >10-8 王小呆的校內互坑賽題解

10-8 王小呆的校內互坑賽題解

read main 簡單的 fin 表達 har ron += open

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;
}

10-8 王小呆的校內互坑賽題解