#74 破解密码
首先容易得出第i位编号为x的字母的公式:
(26^n -1)*x=26*h[i]-h[i+1]
我们可以先求出(26^n -1)%mod的逆元,乘到右边去即可。
可是,这样做只有50分!
(26^n -1)%mod=0的时候没有逆元!!也就是说这种情况下x为任何数都可以,而这种算法会导致全部输出a,h[]全部都是0了,可能与读入的h不符。
因此这种情况的做法是:求出h[1]用26进制表示的n位数,直接输出就是答案了。因为他满足h[1],后面的必然满足。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <string>
#define LL long long
using namespace std;
LL ni;
int n,mod,ans[100005];
LL Pow(LL x,int nn)
{
int n=nn;
LL base=x,ans=1LL;
while (n)
{
if (n&1) ans=ans*base%mod;
base=base*base%mod;
n>>=1;
}
return ans;
}
void Prepare()
{
LL x=1;
for (int i=1;i<=n;i++)
x=1LL*x*26LL%mod;
x=(x-1+mod)%mod;
ni=Pow(x,mod-2);
}
void Solve1()
{
LL x;
scanf("%lld",&x);
for (int i=n;i;i--)
{
ans[i]=x%26;
x/=26;
}
for (int i=1;i<=n;i++)
cout<<(char)(ans[i]+'a');
cout<<endl;
}
int main()
{
scanf("%d%d",&n,&mod);
Prepare();
if (ni==0LL)
{
Solve1();
return 0;
}
LL xx,x,y;
scanf("%lld",&xx);
x=xx;
for (int i=1;i<n;i++)
{
scanf("%lld",&y);
ans[i]=(x*26LL-y+mod)%mod*ni%mod;
while (ans[i]>26)
ans[i]-=mod;
x=y;
}
ans[n]=(x*26LL-xx+mod)%mod*ni%mod;
while (ans[n]>26)
ans[n]-=mod;
for (int i=1;i<=n;i++)
cout<<(char)(ans[i]+'a');
cout<<endl;
return 0;
}
B:
#75 智商锁
基尔霍夫定理+乱搞
基尔霍夫定理可以用O(n^3)时间求出一个无向图的生成树个数。
说一下这个算法的过程:
1.构造出图对应的矩阵,(i,i)的值是i号点的度数,如果i,j之间连边,那么(i,j)=(j,i)=-1
2.任意去掉i行i列,变成n-1阶行列式
3.这个图的行列式的值就是生成树个数:
先高斯消元,结果就是对角线的乘积
乱搞的方法是从7号数据延伸出来的:
7号数据的k都是若干个小质数相乘,那么小质数可以直接构造成环,把环用桥连起来的图的生成树个数就是所求了!
乱搞过程及证明见官方题解。
<span style="font-size: 18px;">#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <map>
#define mod 998244353
#define LL long long
using namespace std;
int tot=0,aaa=0,h[1001010],a[1005][15][15],edge[1005];
LL inv[1005],num[1005],d[15][15];
int T;
struct Edge
{
int x,y,ne,v;
}e[1001010];
void Build(int x)
{
memset(d,0,sizeof(d));
for (int i=1;i<12;i++)
for (int j=i+1;j<=12;j++)
if (rand()%10>=2)
{
a[x][i][j]=a[x][j][i]=1;
d[i][j]=d[j][i]=mod-1;
edge[x]++;
d[i][i]++,d[j][j]++;
}
}
LL Pow(LL x,int n)
{
LL ans=1,base=x;
while (n)
{
if (n&1) ans=ans*base%mod;
base=base*base%mod;
n>>=1;
}
return ans;
}
void Gauss()
{
int i,j,k;
for (i=1;i<12;i++)
{
for (j=i;j<12;j++)
if (d[j][i]!=0)
break;
for (k=i;k<12;k++)
swap(d[i][k],d[j][k]);
LL s=Pow(d[i][i],mod-2);
for (j=i+1;j<12;j++)
{
int rate=(mod-d[j][i]*s%mod)%mod;
for (k=i;k<12;k++)
(d[j][k]+=d[i][k]*rate)%=mod;
}
}
}
void Hash(int x,int y)
{
int s=(int)(num[x]*num[y]%mod);
int k=s%1001001;
for (int i=h[k];i;i=e[i].ne)
if (e[i].v==s) return;
e[++tot].x=x,e[tot].y=y,e[tot].ne=h[k],h[k]=tot,e[tot].v=s;
}
int Get(int x)
{
int k=x%1001001;
for (int i=h[k];i;i=e[i].ne)
if (e[i].v==x) return i;
return 0;
}
void P(int x,int k)
{
for (int i=1;i<=12;i++)
for (int j=i+1;j<=12;j++)
if (a[x][i][j])
printf("%d %d\n",k+i,k+j);
}
void Print(int aa,int bb,int cc,int dd)
{
printf("48 %d\n",edge[aa]+edge[bb]+edge[cc]+edge[dd]+3);
P(aa,0);
P(bb,12);
P(cc,24);
P(dd,36);
printf("12 13\n24 25\n36 37\n");
}
int main()
{
srand(12341234);
scanf("%d",&T);
for (int i=1;i<=1000;i++)
{
Build(i);
Gauss();
num[i]=1;
for (int j=1;j<12;j++)
num[i]=num[i]*d[j][j]%mod;
if (num[i])
inv[i]=Pow(num[i],mod-2);
}
for (int i=1;i<=1000;i++)
if (num[i])
for (int j=i;j<=1000;j++)
if (num[j])
Hash(i,j);
while (T--)
{
int k;
scanf("%d",&k);
if (k==0)
{
printf("10 0\n");
continue;
}
int ok=0;
for (int i=1;i<=1000;i++)
{
if (num[i])
for (int j=i;j<=1000;j++)
if (num[j])
{
LL invv=inv[i]*inv[j]%mod;
int id=Get((int)((LL)k*invv%mod));
if (id)
{ok=1,Print(i,j,e[id].x,e[id].y);break;}
}
if (ok) break;
}
if (!ok)
puts("QwQ");
}
return 0;
}
</span>
C:
#76 懒癌
只会7号数据的十分:
1.只有一只狗生病:这只狗的主人看到所有狗都没有病,那一定是自己的狗病了。
day+=1,dog+=1
2.有两只狗病了:其中一只病狗的主人一开始认为自己的狗没有病,看到一只狗病了,那么这只狗的主人看不到任何一只狗生病,第一天应该开枪;结果他没有开,说明自己的狗病了,第二天开枪。另一只病狗的主人分析同理。
day+=2,dog+=2
3.有三只狗病了:其中一只病狗的主人认为自己的狗没病,那么对于他所看的两只病狗就面临这2中的处境,可是他在第二天并没有听到枪声,因此一定是自己的狗病了,于是在第三天开枪。其他两只同理。
day+=3,dog+=3
。
。
。
那么最后的答案就是:dog=day=sigma(C(n,i)*i)
感悟:
1.用到逆元一定要考虑逆元是否存在!
2.乱搞好神奇~随机好厉害~
3.假设法,推出矛盾。