比赛题目来自各个OJ,经过数据加强
输入格式:
一行,两个整数a, b
输出格式:
一行,一个整数a * b
说明
0 <= a, b <= 2147483648
由于a和b都是<=2147483648的,所以它们乘积可能超长整型哒。。。
然而!用unsigned long long就好了。。。不用写高精度乘法的。
代码:
【过水已隐藏】
输入格式:
一个n,一个m。都是正整数。
输出格式:
gcd(第n个斐波那契数,第m个斐波那契数)
输出这个公约数的后8位就好了(前面的0不要)
说明
1<=n,m<=10^9
这题我给的数据比较水所以这样就能过了:
#include
#include
#include
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
#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;ifor(int j=0;jfor(int k=0;kreturn 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<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;
}
题目描述
在集合中找出 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
#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?bstd::set S;
std::set ::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
#include
#include
#include
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;
}
标算看不懂就算了最好看懂。。。但前面那个非标算总要看看吧?
题目背景
。。。有没有发现标题也是个回文串。。。
题目描述
cyx喜欢积木,上面还有字母,她有n个积木,每个上面都有a~z二十六个字母中的一个。然后她总是喜欢把这些积木从左往右排成一列。她觉得如果有一段连续积木从左/右读是一样的,这就被叫做“和cyx一样机智勇敢聪明可爱纯洁善良的一列积木”。 Then。。。cyx找出了这些列积木并把它们按照每列(可能有重复的积木)积木个数从小到大排,取前k个乘起来。cyx想知道这个乘积模19930726的值。
输入格式:
第一行为两个正整数n和k。 接下来一行为n个字符,代表从左到右积木上写的字母。
输出格式:
输出一个整数。如果总的和cyx一样机智勇敢聪明可爱纯洁善良的一列积木个数小于K,输出一个整数-1。
说明
n<=1e6, k<=1e12
这题出现在上题后真的让人身心愉悦心情舒畅~~~感觉像三个模版题。。。
很明显要用Manacher算法。。。原题太长,改短了。马拉车前缀和快速幂三个一起弄一下就好了。。。竟然还是国家集训队的???!!!
代码如下。
#include
#include
#include
#include
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 (nowprintf("-1");
return 0;
}
printf("%lld",ans);
return 0;
}
题目描述
概念:
有向图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
#include
#include
#include
#include
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]return y[a]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]void tsort() //拓扑排序+递推
{
while(tint u=ue[++t];
for(int i=first[u];i;i=next[i])
{
int v=to[i];
de[v]--;
if(dis[v]//更新临时最长距离+重算方案数
{
dis[v]=dis[u]+si[v];
e[v]=0;
if(dis[ans]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<return 0;
}