牛客网暑期ACM多校训练营(第七场
第七场和第八场比较难,所以补的题少一点。。
C Bit Compression
暴力+剪枝就完事了。
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
int num[20][maxn];
char s[maxn];
int C(int opt,int a,int b)
{
if(opt==0)return a&b;
else if(opt==1)return a|b;
return a^b;
}
int dfs(int n)
{
int x=0;
for(int i=0;i<(1<<n);i++)
x+=num[n][i];
if(x==0)return 0;
if(n==0)return 1;
int res=0;
for(int i=0;i<3;i++)
{
for(int j=0;j<(1<<(n-1));j++)
num[n-1][j]=C(i,num[n][2*j],num[n][2*j+1]);
res+=dfs(n-1);
}
return res;
}
int main()
{
int n;
scanf("%d %s",&n,s);
for(int i=0;i<(1<<n);i++)
num[n][i]=s[i]-'0';
printf("%d\n",dfs(n));
return 0;
}
E Counting 4-Cliques
这题我其实觉得题解并没有说到位,为什么要留下5个点来进行最后的填补,这其实有一定的原因的。
首先,我们可以预处理出n个点的完全图的4元环的个数,也就是C(n,4),那么我们可以发现,70和71个点之间的差距最大,差不多60000,所以我们面临的最大的问题就是如何用5个点来构造60000个四元环,只要这个问题解决了,那么其他的构造就不成问题了。
#include<bits/stdc++.h>
using namespace std;
map<int,int>M;
int C(int n,int m)
{
if(n<m)return 0;
long long up=1;
for(int i=0;i<m;i++)
up=up*(n-i);
long long down=1;
for(int i=1;i<=m;i++)
down=down*i;
return up/down;
}
int vis[70000];
void init()
{
for(int i=4;i<=70;i++)
{
int tmp=C(i,4);
M[tmp]=i;
//cout<<i<<' '<<tmp<<endl;
}
vis[1]=3;
for(int i=4;i<=70;i++)
vis[C(i,3)]=i;
}
int main()
{
init();
int k;
scanf("%d",&k);
auto it=M.upper_bound(k);
it--;
int left=k-(it->first);
if(left==0)
{
int n=(it->second);
printf("%d %d\n",n,n*(n-1)/2);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
printf("%d %d\n",i,j);
}
else
{
vector<int>V;
bool flag=0;
int n=(it->second);
for(int i=2;i<=n&&!flag;i++)
for(int j=2;j<=n&&!flag;j++)
for(int t=2;t<=n&&!flag;t++)
for(int m=2;m<=n&&!flag;m++)
{
int ct=C(i,3)+C(j,3)+C(t,3)+C(m,3);
if(ct>left)break;
int tmp=left-ct;
if(tmp>=70000)continue;
if(tmp==0)
{
flag=1;
if(i>2)V.push_back(i);
if(j>2)V.push_back(j);
if(t>2)V.push_back(t);
if(m>2)V.push_back(m);
break;
}
else if(vis[tmp])
{
flag=1;
if(i>2)V.push_back(i);
if(j>2)V.push_back(j);
if(t>2)V.push_back(t);
if(m>2)V.push_back(m);
V.push_back(vis[tmp]);
break;
}
}
int m=n*(n-1)/2;
n=n+V.size();
for(int i=0;i<V.size();i++)
m+=V[i];
printf("%d %d\n",n,m);
for(int i=1;i<=(it->second);i++)
for(int j=i+1;j<=(it->second);j++)
printf("%d %d\n",i,j);
for(int i=0;i<V.size();i++)
{
int now=i+1+(it->second);
for(int j=0;j<V[i];j++)
printf("%d %d\n",now,j+1);
}
}
return 0;
}
J Sudoku Subrectangles
这题真好。
首先很容易想到52*52*n*m,显然会超时。在分析这题的时候,会发现以一个可行区域来往后推要考虑很多限制。而有些限制是前面会影响后面的。所以我们在枚举的时候沿着限制的影响方向走就会减少时间复杂度,因为这样就不用重新考虑前面的影响了。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
const int inf=0x3f3f3f3f;
char mp[maxn][maxn];
int L[maxn][maxn];
int U[maxn][maxn];
int n,m;
void init()
{
int Nxt[maxn];
for(int i=1; i<=n; i++)
{
memset(Nxt,inf,sizeof(Nxt));
for(int j=m; j>=1; j--)
L[i][j]=min(L[i][j+1]+1,Nxt[mp[i][j]]-j),Nxt[mp[i][j]]=j;
}
for(int i=1; i<=m; i++)
{
memset(Nxt,0,sizeof(Nxt));
for(int j=1; j<=n; j++)
{
U[j][i]=min(U[j-1][i]+1,j-Nxt[mp[j][i]]),Nxt[mp[j][i]]=j;
}
}
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1; i<=n; i++)
scanf("%s",mp[i]+1);
init();
int Len[maxn];
long long ans=0;
for(int i=1;i<=m;i++)
{
memset(Len,inf,sizeof(Len));
for(int j=1;j<=n;j++)
{
for(int k=0;k<L[j][i];k++)
{
Len[k]=min(Len[k]+1,U[j][i+k]);
if(k)Len[k]=min(Len[k],Len[k-1]);
ans+=Len[k];
}
for(int k=L[j][i];k<52;k++)Len[k]=0;
}
}
cout<<ans<<endl;
}
D Inverse Inverse Problem
最后是D题,真的是神仙题啊。。
说实话虽然理解了做法,但碰见肯定还是脑子一片糊啊。。太绕了。。
直接讲做法吧。
首先第一点就是做n次f(x)运算可以有一个通式
第二就是当A可以为1的情况有
1,X=T
2,X!=T&&n%p!=0
因为上面的式子其实还要取模,所以当A=1时,B的系数就为n%P,如果n%p=0,的话那么就只剩下前面的x了,x又不等于T,所以不成立。所以n%p!=0.
那么当A不等于1时,式子就可以化为
假设已经确定A并且A!=1了,B无解的情况就是左边不等于0,右边等于0.那么我们可以使T=1,X=0.右边也就是
这个式子其实是在模p的基础上成立的,严格上看实际上是
那么我们要找到最大的A,小于A的数(2,3,…A-1)x都要满足上面的式子。
而上面的式子实际上就是求x的阶,而x的阶其实是(p-1)的约数,大家应该都知道当n等于p-1时这个式子就会满足情况了。
我们记r(x)为x的阶,那么n=lcm(r(2),r(3),..r(A-1))。这样也就求出n了。
所以做法总结上面的思路,就是枚举小于M的模数p,然后在这个模数下面,枚举(p-1)的约数,看在当前约数下能算出来的A最大是多少就行了。
然而我看别人的代码并没有枚举p-1的约数,他们枚举的都是p-1的质因子b,然后n等于(p-1)/b,我不太明白从中的道理,如果有读者知道的,希望您能联系我。。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
bool is[maxn];
int prm[maxn],id;
int md[maxn];// minimum divisor
void init()
{
memset(is,1,sizeof(is));
is[0]=is[1]=0;
md[1]=1;
int k=0;
prm[k++]=2;
md[2]=2;
for(int i=4;i<maxn;i+=2)is[i]=0,md[i]=2;
int e=(int)sqrt(maxn+0.0);
int i;
for(i=3;i<=e;i+=2)if(is[i])
{
prm[k++]=i;
md[i]=i;
for(int s=2*i,j=i*i;j<maxn;j+=s)
{
is[j]=0;
if(!md[j])md[j]=i;
}
}
for(;i<maxn;i+=2)if(is[i])prm[k++]=i,md[i]=i;
id=k;
}
int factor[100][2];
int fatCnt;
int getFactors(int x)
{
fatCnt=0;
int tmp=x;
for(int i=0;prm[i]<=tmp/prm[i];i++)
{
factor[fatCnt][1]=0;
if(tmp%prm[i]==0)
{
factor[fatCnt][0]=prm[i];
while(tmp%prm[i]==0)
{
factor[fatCnt][1]++;
tmp/=prm[i];
}
fatCnt++;
}
}
if(tmp!=1)
{
factor[fatCnt][0]=tmp;
factor[fatCnt++][1]=1;
}
return fatCnt;
}
int quick_mul(int a,int b,int mod)
{
int res=1;
while(b)
{
if(b%2)res=1LL*res*a%mod;
b/=2;
a=1LL*a*a%mod;
}
return res;
}
typedef tuple<int,int,int>t3;
vector<int>V;
void getfac(int x)
{
V.clear();
for(int i=x;i>1;i/=md[i])
V.push_back(md[i]);
sort(V.begin(),V.end());
V.erase(unique(V.begin(),V.end()),V.end());
}
t3 solve(int idx)
{
int p=prm[idx];
int x=p-1;
getfac(x);
int A=0,n;
for(int i=0;i<V.size();i++)
{
int tmpn=x/V[i];
int nowa=2;
while(nowa<p&&quick_mul(nowa,tmpn,p)==1)nowa++;
if(nowa>A)
{
A=nowa;
n=tmpn;
}
}
return t3(A,n,p);
}
int main()
{
init();
int M;
scanf("%d",&M);
int n=1,A=1;
int p=1;
for(int i=1;i<id;i++)
{
//cout<<i<<endl;
if(prm[i]>M)break;
tuple<int,int,int>tmp=solve(i);
if(A<get<0>(tmp))
tie(A,n,p)=tmp;
}
while(n%p)n+=(p-1);
printf("1 %d 2 %d\n",n,p);
return 0;
}
最后x不能等于0,你换一下就行了,只要使得左边不等于0.
推荐阅读
-
2019牛客暑期多校训练营(第二场)H Second Large Rectangle
-
2020牛客暑期多校训练营(第四场)——Basic Gcd Problem
-
2020牛客暑期多校训练营(第五场)
-
2020牛客暑期多校训练营(第九场) The Flee Plan of Groundhog
-
2020牛客暑期多校训练营Groundhog and Apple Tree(树形dp,贪心)
-
2020暑期牛客多校训练营第九场(K)The Flee Plan of Groundhog(lca,树形dp)
-
2020牛客暑期多校训练营(第二场)Cover the Tree
-
2020牛客暑期多校训练营(第一场)H-Minimum-cost Flow
-
2020牛客暑期多校 第一场 F Infinite String Comparision(字符串)
-
Harmony Pairs 2020牛客暑期多校训练营(第六场)