Examples

2022-7-13 #12 UR6

今天没有考试~~~开心😀

034 uoj#74. 【UR #6】破解密码

简单题。

考虑 \(f(k))\rightarrow f(k+1)\) 的过程:

\[26(f(k)-26^{n-1}\times num(t_k))=f(k+1)-num(t_k) \]

我们可以解出 \(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;
}
posted @ 2022-07-13 08:34  xiaoziyao  阅读(54)  评论(1编辑  收藏  举报