【UOJ #6】AB题解


#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)时间求出一个无向图的生成树个数。


vfk的基尔霍夫定理证明


说一下这个算法的过程:

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.假设法,推出矛盾。


你可能感兴趣的:(OI,乱搞,逆元,uoj,基尔霍夫定理)