比賽題目來自各個OJ,經過數據加強
目錄
T0: 送分水題
輸入格式:
一行,兩個整數a, b
輸出格式:
一行,一個整數a * b
說明
0 <= a, b <= 2147483648
由於a和b都是<=2147483648的,所以它們乘積可能超長整型噠。。。
然而!用unsigned long long就好了。。。不用寫高精度乘法的。
代碼:
【過水已隱藏】
T1: 斐波那契和……歐幾里得(???)1
輸入格式:
一個n,一個m。都是正整數。
輸出格式:
gcd(第n個斐波那契數,第m個斐波那契數)
輸出這個公約數的後8位就好了(前面的0不要)
說明
1<=n,m<=10^9
這題我給的數據比較水所以這樣就能過了:
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
long long n, m, a[1000001];
int gcd(long long x, long long y)
{
if (!min(x, y))
return max(x, y);
return gcd(min(x, y), max(x, y) % min(x, y));
}
int main()
{
cin >> n >> m;
long long q = gcd(n, m);
a[1] = 1;
a[2] = 1;
for (int i = 3; i <= q; i++)
a[i] = (a[i - 1] + a[i - 2]) % 100000000;
cout << a[q];
return 0;
}
這樣就好了。
有這麼一個著名式子:gcd(f(n), f(m))=f(gcd(n, m))。證明有一點難,但是這個式子應該聽說過的吧?
以下是luogu ID 爲淺色調 巨佬的證明:
設n < m, f[n]=a, f[n+1]=b
則f[n+2]=a+b, f[n+3]=a+2b, … f[m]=f[m-n-1]a+f[m-n]b
因爲f[n]=a, f[n+1]=b, f[m]=f[m-n-1]a+f[m-n]b
所以f[m]=f[m-n-1]*f[n]+f[m-n]*f[n+1]
又因爲gcd(f[n], f[m])=gcd(f[n],f[m-n-1]*f[n]+f[m-n]*f[n+1])
而f[n]|f[m-n-1]*f[n]
所以gcd(f[n],f[m-n]*f[n+1])
再證一個引理:gcd(f[n],f[n+1])=1
證:由歐幾里得定理知gcd(f[n],f[n+1])=gcd(f[n],f[n+1]-f[n])=gcd(f[n],f[n-1])=gcd(f[n-2],f[n-1])=……=gcd(f[1],f[2])=1
得證。
由引理和gcd(f[n],f[m])=gcd(f[n],f[m-n]*f[n+1])
所以gcd(f[n],f[m])=gcd(f[n],f[m-n])
即gcd(f[n],f[m])=gcd(f[n],f[m%n])
繼續遞歸,將m1=m%n, 則gcd(f[n],f[m])=gcd(f[n%m1],f[m1])
…
不難發現整個遞歸都在求解gcd(n, m)
最後遞歸到出現f[0]時,此時的f[n]就是所求gcd。
q.e.d.
之前說了出的數據比較水不會TLE。。。但之前的代碼不是標算。
以下巨佬 淺色調 標準答案:(矩陣加速好可怕)
#include <bits/stdc++.h>
#define il inline
#define ll long long
#define mem(p) memset(&p,0,sizeof(p))
using namespace std;
const ll mod=1e8;
ll n,m;
struct mat{ll a[3][3],r,c;};
il mat mul(mat x,mat y)
{
mat p;
mem(p);
for(int i=0;i<x.r;i++)
for(int j=0;j<y.c;j++)
for(int k=0;k<x.c;k++)
p.a[i][j]=(p.a[i][j]+x.a[i][k]*y.a[k][j])%mod;
p.r=x.r,p.c=y.c;
return p;
}
il void fast(ll k)
{
mat p,ans;
mem(p),mem(ans);
p.r=p.c=2;
p.a[0][0]=p.a[0][1]=p.a[1][0]=1;
ans.r=1,ans.c=2;
ans.a[0][0]=ans.a[0][1]=1;
while(k)
{
if(k&1)ans=mul(ans,p);
p=mul(p,p);
k>>=1;
}
cout<<ans.a[0][0];
}
il ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
int main()
{
ios::sync_with_stdio(0);
cin>>n>>m;
n=gcd(n,m);
if(n<=2)cout<<1;
else fast(n-2);
return 0;
}
T3: 毒瘤題_12
題目描述
在集合中找出 k 個出現了奇數次的正整數 a
輸入格式:
第一行兩個正整數n,k,接下來n 行每行一個正整數表示集合內的元素
輸出格式:
從小到大輸出 k 行 k 個數,中間用空格分隔。
說明
n<=3000000, 0< ai<=10的10次方。 所有數據正好有k 個數出現了奇數次且 k≤500 保證出現奇數次的 k個數是在 [0,10的10次方] 中均勻隨機的。
名副其實。。。之前我想把內存限制加大的不然太可怕了,,,然而洛谷說最大256M。。。然後我想到一個內存128M以內但會超時的算法比原來要簡單多了,所以打算把時間限制改爲2s的,然而洛谷說比賽已經開始了不能改了。。。
所以這道題答案正確但時間在2s以內都算對吧。。。不然真的太難了。。。
用set來弄
#include <bits/stdc++.h>
#define M 1000000009
struct tim
{
char a;
unsigned b;
tim(const long long &x=0):a(x>>32),b(x){}
operator long long()const{return (long long)a<<32|b;}
const bool operator <(const tim &B)const{return a==B.a?b<B.b:a<B.a;}
}x;
std::set<tim>S;
std::set<tim>::iterator it;
int main()
{
long long t;
int n,k;
scanf("%d%d",&n,&k);
while(n--)
{
scanf("%lld",&t);
x=t;
if((it=S.find(x))==S.end())
S.insert(x);
else
S.erase(x);
}
for(it=S.begin();k--;)
printf("%lld\n",t=*it++);
return 0;
}
是時候祭出可怕的標算了。。。異或的特殊性質要了解一下。。。bitset要用。。。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<bitset>
using namespace std;
char frBB[1<<12],*frS=frBB,*frT=frBB;
#define getchar() (frS==frT&&(frT=(frS=frBB)+fread(frBB,1,1<<12,stdin),frS==frT)?EOF:*frS++)
#define ll long long int
inline ll read()
{
ll x=0;
char ch=getchar();
while(!isdigit(ch))
{
ch=getchar();
}
while(isdigit(ch))
{
x=x*10+(ch-'0');
ch=getchar();
}
return x;
}
int n,k;
bool used[502]={};
ll x,ans[502]={};
bitset<1000005> a,b;
bitset<9260820> hash;
int stot=0,ttot=0;
int atot=0;
ll xa,xb,xh;
ll s[502]={},t[502]={};
int main()
{
n=read();
k=read();
for(int i=1;i<=n;++i)
{
x=read();
xa=x%1000000;
xb=x/10000;
xh=(x^(x>>2)^(x>>10)^(x<<5))&8388607;
a[xa]=a[xa]^1;
b[xb]=b[xb]^1;
hash[xh]=hash[xh]^1;
}
for(int i=0;i<=1000000;++i)
{
if(a[i])
s[++stot]=1ll*i;
if(b[i])
t[++ttot]=1ll*i;
}
for(int i=1;i<=stot;++i)
for(int j=1;j<=ttot;++j)
{
if(used[j]||(s[i]/10000!=t[j]%100))
continue;
ll xx=t[j]*10000+s[i]%10000;
if(hash[(xx^(xx>>2)^(xx>>10)^(xx<<5))&8388607])
{
ans[++atot]=xx;
used[j]=1;
break;
}
}
sort(ans+1,ans+1+atot);
for(int i=1;i<=atot;++i)
printf("%lld\n",ans[i]);
return 0;
}
標算看不懂就算了最好看懂。。。但前面那個非標算總要看看吧?
T4: 積木積3
題目背景
。。。有沒有發現標題也是個迴文串。。。
題目描述
cyx喜歡積木,上面還有字母,她有n個積木,每個上面都有a~z二十六個字母中的一個。然後她總是喜歡把這些積木從左往右排成一列。她覺得如果有一段連續積木從左/右讀是一樣的,這就被叫做“和cyx一樣機智勇敢聰明可愛純潔善良的一列積木”。 Then。。。cyx找出了這些列積木並把它們按照每列(可能有重複的積木)積木個數從小到大排,取前k個乘起來。cyx想知道這個乘積模19930726的值。
輸入格式:
第一行爲兩個正整數n和k。 接下來一行爲n個字符,代表從左到右積木上寫的字母。
輸出格式:
輸出一個整數。如果總的和cyx一樣機智勇敢聰明可愛純潔善良的一列積木個數小於K,輸出一個整數-1。
說明
n<=1e6, k<=1e12
這題出現在上題後真的讓人身心愉悅心情舒暢~~~感覺像三個模版題。。。
很明顯要用Manacher算法。。。原題太長,改短了。馬拉車前綴和快速冪三個一起弄一下就好了。。。竟然還是國家集訓隊的???!!!
代碼如下。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,dp[1000001],p[1000001];
char ch[1000001];
long long m,sum[1000001];
void manacher()
{
int mx=0,id;
for (int i=1;i<=n;i++)
{
if (mx>=i)
p[i]=min(mx-i,p[2*id-i]);
else
p[i]=0;
for (;ch[i+p[i]+1]==ch[i-p[i]-1];p[i]++);
if (p[i]+i>mx)
mx=p[i]+i,id=i;
sum[0]++,sum[p[i]+1]--;
}
}
long long quickpow(long long num,long long x)
{
long long base=num%19930726,ans=1;
while (x)
{
if (x%2==1)
ans=(ans*base)%19930726;
x>>=1;
base=(base*base)%19930726;
}
return ans;
}
int main()
{
scanf("%d%lld",&n,&m);
scanf("%s",ch+1);
ch[0]='#';
manacher();
for (int i=1;i<=n;i++)
sum[i]=sum[i-1]+sum[i];
long long ans=1,now=0;
for (int i=n/2+1;i>=0;i--)
{
if (sum[i]==0)
continue;
ans=(ans*quickpow(i*2+1,min(sum[i],m-now)))%19930726;
now+=sum[i];
if (now>=m)
break;
}
if (now<m)
{
printf("-1");
return 0;
}
printf("%lld",ans);
return 0;
}
T5: 毒瘤題_24
題目描述
概念:
有向圖G=(V,E),圖中任意兩點a,b,存在一條a->b或者b->a的單向路徑,那麼G是半連通的。
有向圖G‘=(V’,E‘)滿足V‘是V的子集,E’是E中和V‘有關的邊,那麼G’是G的導出子圖。
有向圖G‘是G的導出子圖,而且G’半連通,那麼G‘是G的半連通子圖。
有向圖G’是G所有半連通子圖中包含節點數最多的,則稱G‘是G的最大半連通子圖。
給定一個有向圖G,求G的最大半連通子圖擁有的節點數MAXV,以及不同的最大半連通子圖的數目C。僅要求輸出C對X的餘數。
輸入格式:
三個整數N,M,X。N,M分別表示G的點數和邊數。 接下來M行,每行2個正整數a,b,表示一條有向邊(a,b)。圖中每個點將編號爲1,2,3……N。
輸入中同一個(a,b)不會出現兩次
輸出格式:
第一行一個整數K。 第二行包含整數C Mod X。
說明
N<=1e5,M<=1e6,X<=1e8
代碼有點長,但經常寫寫LCT / FFT或者做過這一題或者做過什麼項目的巨神肯定覺得代碼巨短啊。。。
主要用的:Tarjan+toposort+DP。
首先Tarjan縮個點,去重連邊,然後新圖get。題目就成了讓你求圖中最長鏈和最長鏈的個數了。。。
最長鏈直接用拓撲排序,最長鏈的個數求法有點像DP,代碼中用f[i]表示新圖裏以i爲終點的方案數,那麼f[i]就等於連到i並且還滿足距離=起點到i的臨時最長距離的點的f之和,最後查找距離等於最長鏈的點,答案是方案數量之和。
祭出代碼:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1e5+5,maxm=1e6+5;
int n,m,mo,total,num,top,col,t,w,ans;
int x[maxm],y[maxm],to[maxm],next[maxm],nu[maxm];
int de[maxn],first[maxn],ue[maxn],si[maxn],dfn[maxn],low[maxn],st[maxn],co[maxn],e[maxn],dis[maxn];
int _read()
{
int x=0;
char c=getchar();
while('0'>c || c>'9')
c=getchar();
while('0'<=c && c<='9')
{
x=(x<<3)+(x<<1)+c-'0';
c=getchar();
}
return x;
}
void ins(int x,int y) //連接一條x到y的邊
{
next[++total]=first[x];
first[x]=total;
to[total]=y;
}
void tarjan(int u) //縮點
{
dfn[u]=low[u]=++num;
st[++top]=u;
for(int i=first[u];i;i=next[i])
{
int v=to[i];
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else
if(!co[v])
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
co[u]=++col;
si[col]++;
while(st[top]!=u)
si[col]++,co[st[top]]=col,top--;
top--;
}
}
bool cmp(int a,int b)
{
if(x[a]!=x[b])
return x[a]<x[b];
return y[a]<y[b];
}
void _remove() //去重邊(不然可能會影響到方案數qaq)
{
for(int i=1;i<=m;++i)
{
nu[i]=i;
x[i]=co[x[i]];
y[i]=co[y[i]];
}
sort(nu+1,nu+1+m,cmp);
}
void _build() //縮點重建圖+處理入度準備拓撲排序
{
total=0;
memset(first,0,sizeof(first));
for(int i=1;i<=m;++i)
{
int z=nu[i];
if((x[z]!=y[z]) && (x[z]!=x[nu[i-1]] || y[z]!=y[nu[i-1]]))
{
de[y[z]]++;
ins(x[z],y[z]);
}
}
}
void _reset() //拓撲排序初始入隊
{
for(int i=1;i<=col;++i)
if(!de[i])
{
ue[++w]=i;
dis[i]=si[i];
e[i]=1;
if(dis[ans]<dis[i])
ans=i;
}
}
void tsort() //拓撲排序+遞推
{
while(t<w)
{
int u=ue[++t];
for(int i=first[u];i;i=next[i])
{
int v=to[i];
de[v]--;
if(dis[v]<dis[u]+si[v]) //更新臨時最長距離+重算方案數
{
dis[v]=dis[u]+si[v];
e[v]=0;
if(dis[ans]<dis[v])
ans=v;
}
if(dis[v]==dis[u]+si[v]) //累加
e[v]=(e[u]+e[v])%mo;
if(!de[v])
ue[++w]=v;
}
}
}
int anss;
void _ask() //統計答案
{
for(int i=1;i<=n;++i)
if(dis[i]==dis[ans])
{
anss=(anss+e[i])%mo;
}
}
int main()
{
n=_read();
m=_read();
mo=_read();
for(int i=1;i<=m;++i)
{
x[i]=_read(),y[i]=_read();
ins(x[i],y[i]);
}
for(int i=1;i<=n;++i)
if(!dfn[i])
tarjan(i);
_remove();
_build();
_reset();
tsort();
_ask();
cout<<dis[ans]<<endl<<anss;
return 0;
}