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