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;
}
10-8 王小呆的校內互坑賽題解