群赛 round#6 解题报告
赛制: OI 难度: noip
得分: math 90/100
cover 0/100
monogatari 0/100
T1 原根(math)
原根是一个数学概念,不知道的可以参见百度百科:原根
这道题我首先预处理出指数和n的欧拉函数值,然后根据n=1,2,4,p,2p,p^n(p为奇质数,n∈N*)来初步判断n是否有原根.之后,利用phi判断(2~n-1)^i%n是否会等于1,如果都符合条件的话,求出原根并输出.不过特别地,phi(1)=1,所以1的原根是1,我因为这一点wa了一个点.
代码如下:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<algorithm>
#include<iostream>
#define ll long long
using namespace std;
bool f[10005];
vector<int> g;
int v[10005],n,id;
int fi(int x)
{
if(f[x])return x-1;
int ans=x;
for(int i=2;i<=x;i++)
if(x%i==0)
{
while(x%i==0) x/=i;
ans=ans-ans/i;
}
if(x>1) ans=ans-ans/x;
return ans;
}
int ksm(int x,int p,int mod)
{
ll s=1,a=x;
while(p)
{
if(p&1) s=(s*a)%mod;
a=a*a%mod;
p>>=1;
}
return (int)s;
}
void cal(int x)
{
g.clear();
if(f[x]) return;
else{
for(int i=2;i*i<=x;i++)
if(x%i==0)
{
g.push_back(i);
if(i*i!=x) g.push_back(x/i);
}
}
}
bool can(int n)
{
if(n%2==0) n/=2;
if(f[n]) return 1;
for(int i=3;i*i<=n;i+=2)
{
if(n%i==0)
{
while(n%i==0) n/=i;
return n==1;
}
}
return 0;
}
void solve(int n)
{
if(n==2 || n==1)
{
cout<<"1"<<endl;return;
}
if(n==4)
{
cout<<"3"<<endl;return;
}
if(!can(n))
{
cout<<"-1"<<endl;return;
}
int p=fi(n);cal(p);
int x=-1;
for(int i=2;i<n;i++)
{
bool flag=1;
if(ksm(i,p,n)!=1) continue;
for(int j=0;j<g.size();j++)
{
if(ksm(i,g[j],n)==1)
{
flag=0;break;
}
}
if(flag)
{
v[0]=x=i;
id++;
break;
}
}
if(x==-1)
{
cout<<x<<endl;return;
}
for(int i=2;i<p;i++)
{
if(__gcd(i,p)==1) v[id++]=ksm(x,i,n);
}
sort(v,v+id);
int now=v[0];
bool u[10005];
for(int i=1;i<id;i++)
{
if(v[i]!=v[0]) now=v[i];
else u[i]=1;
}
for(int i=0;i<id;i++) if(!u[i]) printf("%d\n", v[i]);
}
int main()
{
freopen("math.in","r",stdin);
freopen("math.out","w",stdout);
memset(f,1,sizeof(f));
f[0]=f[1]=0;
for(int i=2;i<10005;i++)
if(f[i]) for(int j=i+i;j<10005;j+=i) f[j]=0;
scanf("%d",&n);
solve(n);
}
利用原根的性质,我们还可以进行简化如下:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
int m;
int phi_m;
int main()
{
freopen("math.in", "r", stdin);
freopen("math.out", "w", stdout);
cin >> m;
phi_m = 0;
for (int i = 1; i < m; ++i) if (__gcd(m, i) == 1) ++phi_m;
int flag = false;
for (int i = 1; i <= m; ++i)
if (__gcd(i,m) == 1)
{
int x = 1;
int d = 0;
for (int j = 1; j <= phi_m; ++j)
{
x = x * i % m;
if (x == 1)
{
d = j;
break;
}
}
if (d == phi_m)
{
flag = true;
printf("%d\n",i);
}
}
if (!flag)
printf("-1\n");
return 0;
}
T2 道路覆盖(cover)
好吧这个题我比赛时没写出来,因为死磕了T3,还错了…╮(╯▽╰)╭
这个题提到"最低高度最高",因此要用二分.用dp[i][j](j=2^k)表示到第i位时前k位的状态为j时的最小花费,二分高度并计算.
代码如下:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
#define ll long long
using namespace std;
int n,m,k;
int h[105],e[105],c[105];
int dp[105][2055];
int min(int x,int y)
{
if(x==-1) return y;if(y==-1) return x;
if(x<y) return x;
else return y;
}
int ok(int mm)
{
memset(dp,-1,sizeof(dp));dp[0][0]=0;
for(int i=0;i<n;i++)
for(int j=0;j<2*k;j++)
if(dp[i][j]!=-1)
{
int sum=0;
for(int t=k-1;t>0;t--) if((1<<t)&j==1) sum+=e[i-k+1+t];
if(sum+h[i+1]>=mm) dp[i+1][j>>1]=min(dp[i+1][j>>1],dp[i][j]);
if(sum+h[i+1]+e[i+1]>=mm) dp[i+1][(1<<(k-1))|(j>>1)]=min(dp[i+1][(j>>1)|(1<<(k-1))],dp[i][j]+c[i+1]);
}
for(int j=0;j<2*k;j++) if(dp[n][j]!=-1 && dp[n][j]<=m) return 1;
return 0;
}
int main()
{
freopen("cover.in","r",stdin);
freopen("cover.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++) scanf("%d%d%d",&h[i],&e[i],&c[i]);
int l=0,r=1e8;
while(l<r-1)
{
int m=(l+r)/2;
if(ok(m)) l=m;
else r=m;
}
if(ok(l+1)) l++;
cout<<l<<endl;
}
T3 物语(monogatari)
这题我写了spfa,不过只写了单向的,所以dis[n]的值一直都没变.
正确的做法首先把第M个边去掉求最短路,spfa求出1到任何一个点的最短路,反向spfa任何一个点到n的最短路.然后当第M个边(s,t)的长度变化的时候,只需要比较dis[1][s]+lin[s][t]+dis[t][n],dis[1][n]即可.
代码如下:
#include<iostream>
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<string.h>
#include<queue>
#include<vector>
#define ll long long
#define inf 1000000001
using namespace std;
int n,m,k,u,v,w,u0,v0,x[30005],vis[200005],dis[200005],d2[200005];
struct nod{
int nex,w;
};
vector<nod>lin[400005];
queue<int> q;
void spfa(int x)
{
while(!q.empty()) q.pop();
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++) dis[i]=inf;
q.push(x);vis[x]=1;dis[x]=0;
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=0;i<lin[u].size();i++)
{
nod v=lin[u][i];
if(dis[v.nex]>dis[u]+v.w)
{
dis[v.nex]=dis[u]+v.w;
if(vis[v.nex]==0)
{
vis[v.nex]=1;
q.push(v.nex);
}
}
}
}
}
int main()
{
freopen("monogatari.in","r",stdin);
freopen("monogatari.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<m;i++)
{
scanf("%d%d%d",&u,&v,&w);
lin[u].push_back(nod{v,w});
lin[v].push_back(nod{u,w});
}
scanf("%d%d",&u0,&v0);
spfa(1);
memcpy(d2,dis,sizeof(d2));
spfa(n);
for(int i=1;i<=k;i++)
{
scanf("%d",&x[i]);
int z=min(d2[n],min(d2[u0]+dis[v0],d2[v0]+dis[u0])+x[i]) ;
if(z==inf) printf("+Inf\n");
else printf("%d\n",z);
}
}
明天要分班考试了,祝我RP++.
rating after round #6: 1526.
本人有轻微的压行习惯,敬请谅解.
本文提供了群赛Round#6的解题报告,详细介绍了三道题目的解题思路与代码实现,包括数学题原根的求解、道路覆盖问题的二分+动态规划方法,以及物语问题中使用SPFA算法进行最短路径计算。
1690

被折叠的 条评论
为什么被折叠?



