2022-7-13 #12 UR6
今天没有考试~~~开心😀
034 uoj#74. 【UR #6】破解密码
简单题。
考虑 \(f(k))\rightarrow f(k+1)\) 的过程:
我们可以解出 \(num(t_k)=\frac{26f(k)-f(k+1)}{26^n-1}\)。
但是有可能 \(p\mid 26^n-1\),此时 \(f(k+1)=26f(k)\),我们输出 \(f(0)\) 的 \(26\) 进制形式即可。
#include<stdio.h>
const int maxn=100005;
int n,mod,mul=1;
int f[maxn],ans[maxn];
int ksm(int a,int b){
int res=1;
while(b){
if(b&1)
res=1ll*res*a%mod;
a=1ll*a*a%mod,b>>=1;
}
return res;
}
int main(){
scanf("%d%d",&n,&mod);
for(int i=1;i<=n;i++)
scanf("%d",&f[i]);
for(int i=1;i<=n;i++)
mul=26ll*mul%mod;
if(mul>1){
int iv=ksm(mul-1,mod-2);
for(int i=1;i<=n;i++)
ans[i]=1ll*(26ll*f[i]-f[i%n+1]+mod)%mod*iv%mod;
}
else for(int i=n;i>=1;i--)
ans[i]=f[1]%26,f[1]/=26;
for(int i=1;i<=n;i++)
putchar(ans[i]+97);
return 0;
}
035 uoj#75. 【UR #6】智商锁
当 \(k\) 比较小的时候,我们可以输出一个大小为 \(k\) 的环。
当 \(k\) 可以表述成小素数之积时,我们可以给每个小素数造一个环,然后用边串在一起。
类似地,我们可以随机生成若干个(\(1000\) 左右)无向图(令第 \(i\) 个无向图生成树个数为 \(f_i\)),然后用边把四个无向图 \(a,b,c,d\) 串在一起,此时 \(f_a\times f_b\times f_c\times f_d\equiv k\pmod {998244353}\)。
采用 meet in middle,类似 BSGS 地枚举前两个存在哈希表里,询问的时候枚举后两个查就好了。
#include<stdio.h>
#include<random>
#include<time.h>
#include<assert.h>
#include<unordered_map>
using namespace std;
const int maxn=15,mod=998244353,T=700,maxs=T+5,maxss=T*(T+1)/2+5;
int q,n=12,k,tot;
int K[maxn][maxn],t[maxs],f[maxs],nf[maxs],g[maxs][maxn][maxn],x[maxss],y[maxss];
unordered_map<int,int>mp;
int ksm(int a,int b){
int res=1;
while(b){
if(b&1)
res=1ll*res*a%mod;
a=1ll*a*a%mod,b>>=1;
}
return res;
}
int calc(){
int ans=1,flg=0;
for(int i=2;i<=n;i++){
int k=i;
for(int j=i+1;j<=n;j++)
if(K[k][i]<K[j][i])
k=j;
if(i!=k){
for(int j=2;j<=n;j++)
swap(K[i][j],K[k][j]);
flg^=1;
}
if(K[i][i]==0)
return 0;
for(int j=i+1;j<=n;j++){
if(K[j][i]>K[i][i]){
for(int k=2;k<=n;k++)
swap(K[i][k],K[j][k]);
flg^=1;
}
while(K[j][i]){
int tmp=K[i][i]/K[j][i];
for(int k=i;k<=n;k++)
K[i][k]=(K[i][k]+1ll*(mod-tmp)*K[j][k]%mod)%mod;
for(int k=2;k<=n;k++)
swap(K[i][k],K[j][k]);
flg^=1;
}
}
ans=1ll*ans*K[i][i]%mod;
}
return flg==0? ans:(mod-ans)%mod;
}
void out(int k,int delta){
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(g[k][i][j])
printf("%d %d\n",i+delta,j+delta);
}
int main(){
mt19937 rnd(time(0));
for(int s=1;s<=T;s++){
t[s]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
K[i][j]=0;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(rnd()%10<7)
K[i][j]++,K[j][i]++,K[i][i]--,K[j][j]--,g[s][i][j]=1,t[s]+=g[s][i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
K[i][j]=(K[i][j]+mod)%mod;
f[s]=calc();
if(f[s]==0)
s--;
else nf[s]=ksm(f[s],mod-2);
}
for(int i=1;i<=T;i++)
for(int j=i;j<=T;j++)
tot++,x[tot]=i,y[tot]=j,mp[1ll*f[i]*f[j]%mod]=tot;
scanf("%d",&q);
while(q--){
scanf("%d",&k);
if(k==0){
puts("2 0");
continue;
}
int a=-1,b=-1,c=-1,d=-1;
for(int i=1;i<=T;i++)
for(int j=i;j<=T;j++){
int p=1ll*k*nf[i]%mod*nf[j]%mod;
if(mp.count(p))
a=i,b=j,c=x[mp[p]],d=y[mp[p]];
}
assert(a!=-1&&b!=-1&&c!=-1&&d!=-1);
printf("%d %d\n",4*n,t[a]+t[b]+t[c]+t[d]+3);
out(a,0),out(b,n),out(c,2*n),out(d,3*n);
printf("%d %d\n%d %d\n%d %d\n",1,1+n,1+n,1+2*n,1+2*n,1+3*n);
}
return 0;
}
036 uoj#76. 【UR #6】懒癌
好怪的一道题。
下面称得了懒癌的狗对应的人为黑点,剩余为白点。
完全图的情况很简单,有 \(k\) 个黑点,这 \(k\) 个点会同时在 \(k\) 时刻开枪。
我们考虑一个人是怎么推断的,他会枚举每一种符合他观察且自己为白点的情况,如果过了这些情况对应开枪时刻的最大值,他就会开枪。
于是我们可以得到一个 \(O(4^nn)\) 的状压 dp 做法,令 \(f_i\) 为生病集合为 \(i\) 时的开枪时间,我们枚举这个集合里每个人,枚举其为白点的所有方案,然后对每个方案的开枪时刻最大值取 \(\min\)。
实际上可以证明,若黑点集合 \(S\subseteq T\),则有 \(f_S\leqslant f_T\),每个人只需假设看不到的人均为黑点,复杂度 \(O(2^nn)\)。
当然这样转移的时候可能成环,但成环意味着“\(i\) 看不到 \(j\)”这一限制成环了,我们只需拓扑排序,将所有成环的位置删掉即可。
我们将这个转移过程用上一行建出的图上的操作刻画,可得:图上有黑点、白点,我们每次可以选择一个黑点变白,然后将其到达的所有点染黑,当所有点都是白点时停止,开枪时间就是最大的操作轮数。(注意这一操作对 \(\min\) 的刻画在“一个人只能被染黑一次”。)
那么,开枪时间就是被染黑的点数量,死亡的狗数量就是没有黑点能到达的黑点数量。(给 dp 值贡献最小的人数,对应如果有黑点能把它染黑,它推断开枪时刻就会比这个黑点后)
考虑每个点的贡献,若第 \(i\) 个点能被 \(c\) 个点到达,则第一问的贡献为 \((2^c-1)2^{n-c}\),第二问的贡献为 \(2^{n-c}\),注意这里的 \(n\) 删掉了成环的点。
#include<stdio.h>
#include<bitset>
#include<iostream>
#include<queue>
using namespace std;
const int maxn=3005,mod=998244353;
int n,ans1,ans2,tot;
int g[maxn][maxn],ord[maxn],deg[maxn],mul[maxn];
bitset<maxn>b[maxn];
string s;
queue<int>q;
int main(){
scanf("%d",&n);
mul[0]=1;
for(int i=1;i<=n;i++)
mul[i]=(mul[i-1]+mul[i-1])%mod,b[i][i]=1;
for(int i=1;i<=n;i++){
cin>>s;
for(int j=0;j<n;j++)
g[j+1][i]=(s[j]-48)^1;
g[i][i]=0;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
deg[j]+=g[i][j];
for(int i=1;i<=n;i++)
if(deg[i]==0)
q.push(i);
while(!q.empty()){
int x=q.front();
q.pop(),ord[++tot]=x;
for(int i=1;i<=n;i++)
if(g[x][i]){
deg[i]--;
if(deg[i]==0)
q.push(i);
}
}
for(int i=tot;i>=1;i--)
for(int j=1;j<=tot;j++)
if(g[ord[j]][ord[i]])
b[ord[j]]|=b[ord[i]];
for(int i=1;i<=tot;i++){
int x=ord[i],c=b[x].count();
ans1=(ans1+1ll*mul[tot-c]*(mul[c]-1))%mod,ans2=(ans2+mul[tot-c])%mod;
}
printf("%d %d\n",ans1,ans2);
return 0;
}