0x3B「数学知识」练习
POJ2480 Longge's problem
要求。
考虑满足gcd(x,n)=1的x的数目就是,那么满足gcd(x,n)=p的x的个数就是满足gcd(x/p,n/p)=p/p=1的x/p的个数,就是
。
因此,为了使n/p为整数,我们考虑对于n的每个约数p,它对答案的贡献就是p*(1~n中满足gcd(x,n)=p的x的个数)。也就是说每个约数p的贡献就是。
代码如下
/*
*/
#define method_1
#ifdef method_1
/*
满足gcd(x, n) = 1 的个数是欧拉函数φ(n),那么可以知道,满足gcd(x, n) = p 的个数可以这么求:
x 和 n 同时除以 p ,那么gcd(x/p, n/p) = 1 ,那么个数就是φ(n/p)
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
const int maxn=65536+5;
const int INF=0x3f3f3f3f;
int n;
int phi(int x){
int ans=x;
int m=(int)sqrt(x+0.5);
for(int i=2;i<=m;i++){
if(x%i==0){
ans=ans/i*(i-1);
while(x%i==0) x/=i;
}
}
if(x>1) ans=ans/x*(x-1);
return ans;
}
int main() {
ios::sync_with_stdio(false);
// freopen("Longge's problem.in","r",stdin);
// cout<<phi(12)<<endl;
while(cin>>n){
ll ans=0;
int m=(int)sqrt(n+0.5);
for(int i=1;i<=m;i++){
if(n%i==0){
ans+=(ll)phi(n/i)*i;
if(n/i!=i) ans+=(ll)phi(i)*(n/i);
}
// if(n/i!=i) ans+=(ll)phi(i)*(n/i); 注意这句话不是放在这里啊
}
cout<<ans<<endl;
}
return 0;
}
#endif
#ifdef method_2
/*
*/
#endif
#ifdef method_3
/*
*/
#endif
POJ1061 青蛙的约会
显然,题目要求满足条件的最小非负整数t。若t不存在,则输出“Impossible”。
将上式变形得
设,即倍数为-Y
移项得
根据贝祖定理,若,则存在t满足条件。
跑一边exgcd就是答案(通过exgcd跑出的x乘以(y-x)/gcd(m-n,l)和对(l/gcd(m-n,l))取模,将答案调整到满足条件的最小非负值),注意讨论m-n的符号。
代码如下
/*
*/
#define method_1
#ifdef method_1
/*
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
const int maxn=+5;
const int INF=0x3f3f3f3f;
ll _x,_y,n,m,l,x,y,a,b;
ll exgcd(ll a,ll b,ll &x,ll &y){
if(!b){
x=1,y=0;
return a;
}
ll d=exgcd(b,(a+b)%b,x,y); //第二个参数不要写成a%b 否则d可能为负数
ll z=x;
x=y,y=z-y*(a/b);
return d;
}
int main() {
ios::sync_with_stdio(false);
//freopen("青蛙的约会.in","r",stdin);
cin>>_x>>_y>>m>>n>>l;
if(_y<_x){
swap(_x,_y),swap(n,m);
}
int d;
if((_y-_x)%(d=exgcd(m-n,l,x,y))==0){
x*=(_y-_x)/d;
while(x<0){
x+=l/d;
}
cout<<x%(l/d);
}
else{
cout<<"Impossible";
}
return 0;
}
#endif
#ifdef method_2
/*
*/
#endif
#ifdef method_3
/*
*/
#endif
3B04 Xiao 9*大战朱最学
中国剩余定理模板题,不再赘述
代码如下
/*
*/
#define method_1
#ifdef method_1
/*
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
const int maxn=10+5;
const int INF=0x3f3f3f3f;
ll M=1,m[maxn],a1[maxn],n,a,b,x,y,ans=0;
ll exgcd(ll a,ll b,ll &x,ll &y){
if(!b){
x=1,y=0;
return a;
}
ll d=exgcd(b,(a+b)%b,x,y);
ll z=x;
x=y,y=z-(a/b)*y;
return d;
}
int main() {
ios::sync_with_stdio(false);
//freopen("大战朱最学.in","r",stdin);
cin>>n;
for(int i=1;i<=n;i++)cin>>m[i]>>a1[i],M*=m[i];
for(int i=1;i<=n;i++){
int t=exgcd(M/m[i],m[i],x,y);
x=((x%m[i])+m[i])%m[i];
ans=(ans+M/m[i]*a1[i]*x)%M;
}
cout<<ans;
return 0;
}
#endif
#ifdef method_2
/*
*/
#endif
#ifdef method_3
/*
*/
#endif
POJ2947 Widget Factory
线性同余方程组,要求所有解都在[3,9]内,且为整数。
先不管同余问题,跑一边高斯消元。
由于方程数和变量数不一定相等,所以用两个变量i和j,记录当前处理过的方程数和处理过的变量数。
跑完高斯消元后,如果此时后面没有处理过的方程中出现常数矩阵非零的情况,就意味着出现了矛盾,即无解。
如果不是无解,而且没有处理完所有变量,方程就已经全部处理完了,或者处理的有效方程数和变量数不同,就是出现了自由元,即存在多组可行解的情况。
最后考虑构造一组[3,9]内的解:由于此时系数矩阵已经是对角矩阵,所以我们可以逆序枚举所有变量和所有方程,依次尝试[3,9]之间的所有数字,判断这个变量能否取这个值,若能,就让它取这个值即可。
代码如下
/*
*/
#define method_1
#ifdef method_1
/*
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
const int maxn=300+5;
const int INF=0x3f3f3f3f;
int n,m,i,j,l,k,c[maxn][maxn],a[maxn],b[maxn],num,x;
char s1[10],s2[10];
int cal(char s[]) {
if(s[0]=='M') return 1;
if(s[0]=='T'&&s[1]=='U') return 2;
if(s[0]=='W') return 3;
if(s[0]=='T'&&s[1]=='H') return 4;
if(s[0]=='F') return 5;
if(s[0]=='S'&&s[1]=='A') return 6;
if(s[0]=='S'&&s[1]=='U') return 7;
}
void solve() {
i=j=1;
while(i<=m&&j<=n) { //m个方程 每个方程最多n个未知数
for(l=i; l<=m; l++) if(c[l][j]) break; //找到含有第j个变元的方程
if(l>m) {
j++;
continue;
}
int x=c[l][j];
for(k=j; k<=n; k++) {
swap(c[i][k],c[l][k]);
}
swap(b[i],b[l]);//将含有第j个变元的方程从第l行换到第i行
for(k=i+1; k<=m; k++) { //用第i行的方程 去消去后面所有方程中的第j个变元
int y=c[k][j];
for(l=j; l<=n; l++) {
c[k][l]=((c[k][l]*x-c[i][l]*y)%7+7)%7;
}
b[k]=((b[k]*x-b[i]*y)%7+7)%7;
}
i++,j++;
}
for(k=i; k<=m; k++)
if(b[k]) {
cout<<"Inconsistent data."<<endl;
return;
}
if(i!=j||j<=n) {
cout<<"Multiple solutions."<<endl;
return;
}
for(i--,j--;i;i--,j--){ //这时的c矩阵为对角矩阵
for(l=0,k=j+1;k<=n;k++) l+=c[i][k]*a[k];
for(k=3;k<=9;k++){
if(((c[i][j]*k)%7+7)%7==((b[i]-l)%7+7)%7) a[j]=k;
}
}
for(i=1; i<n; i++) cout<<a[i]<<" ";
cout<<a[n]<<endl;
}
int main() {
ios::sync_with_stdio(false);
//freopen("Widget Factory.in","r",stdin);
while(cin>>n>>m) {
if(n==0&&m==0) break;
memset(c,0,sizeof(c));
for(i=1; i<=m; i++) {
cin>>num>>s1>>s2;
b[i]=cal(s2)-cal(s1)+1;
for(j=1; j<=num; j++) {
cin>>x;
c[i][x]++;
}
for(j=1; j<=n; j++) {
c[i][j]=(c[i][j]%7+7)%7;
}
}
solve();
}
return 0;
}
#endif
3B13 守卫者的挑战
期望dp,根据题目提示,可以做出类似背包的状态定义。
状态定义:f[i,j,k]表示前i项挑战,成功了j项,背包剩余容量为k的概率(用坐标平移解决k为负数情况)。
状态转移:分第i项挑战成功和挑战失败分别讨论。
PS:代码中有一处注释的写法是错的,目前原因未知。
代码如下
/*
*/
#define method_1
#ifdef method_1
/*
http://www.pianshen.com/article/362730004/
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
const int maxn=200+5;
const int base=200;
const int INF=0x3f3f3f3f;
double p[maxn],f[maxn][maxn][maxn*2],ans=0.0;
int a[maxn],n,l,K;
int main() {
// ios::sync_with_stdio(false);
//freopen("守卫者的挑战.in","r",stdin);
cin>>n>>l>>K;
if(K>n) K=n;
for(int i=1;i<=n;i++) cin>>p[i],p[i]/=100.0;
for(int i=1;i<=n;i++) cin>>a[i];
f[0][0][K+base]=1.0;//f[i,j,k]表示前i项挑战 成功了j项 背包剩余容量为k的概率
for(int i=1;i<=n;i++){
for(int j=0;j<=i;j++){
for(int k=-n;k<=n;k++){
f[i][j][k+base]+=f[i-1][j][k+base]*(1-p[i]); //失败
if(j+1<=i) f[i][j+1][min(k+a[i],n)+base]+=f[i-1][j][k+base]*p[i];//成功
// if(j+1<=i) f[i][j][k+base]+=f[i-1][j-1][k-a[i]+base]*p[i];//这样写不行 原因未知
}
}
}
for(int i=l;i<=n;i++){
for(int j=0;j<=n;j++){
ans+=f[n][i][j+base];
}
}
printf("%.6lf",ans);
return 0;
}
#endif
#ifdef method_2
/*
*/
#endif
#ifdef method_3
/*
*/
#endif
3B14 换教室
NOIP的题目,floyd+期望dp。
细节挺多,但逻辑不难,仔细思考即可。
状态定义:f[i,j,k]表示前i个时间段申请了j个教室 k=0:i不换 k=1:i换的最小体力期望。
状态转移:分四类情况:j为申请的次数
1 i和i-1都没有申请 j>=0
2 i没有申请 i-1申请 j>=1
a i-1申请成功 妞妞从d[i-1]走到c[i]
b i-1申请失败 妞妞从c[i-1]走到c[i]
3 i申请 i-1没有申请 j>=1
a i申请失败 妞妞从c[i-1]走到c[i]
b i申请成功 妞妞从c[i-1]走到d[i]
4 i和i-1都申请 j>=2
a i-1申请失败 i申请失败 妞妞从c[i-1]走到c[i]
b i-1申请成功 i申请成功 妞妞从d[i-1]走到d[i]
c i-1申请失败 i申请成功 妞妞从c[i-1]走到d[i]
d i-1申请成功 i申请失败 妞妞从d[i-1]走到c[i]
注意:这道题计算的是期望,所以所有状态应该是加起来 而不是取min。
代码如下
/*
*/
#define method_1
#ifdef method_1
/*
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
const int maxn=2000+5;
const int maxv=300+5;
const int INF=0x3f3f3f3f;
int n,m,v,e,c[maxn],d[maxn];
double f[maxn][maxn][2],p[maxn],G[maxv][maxv];
int main() {
// ios::sync_with_stdio(false);
//freopen("换教室.in","r",stdin);
cin>>n>>m>>v>>e;
for(int i=1;i<=n;i++) for(int j=0;j<=m;j++) f[i][j][0]=f[i][j][1]=10000000.0;
for(int i=1;i<=v;i++) for(int j=1;j<=v;j++) G[i][j]=G[j][i]=10000000.0;
for(int i=1;i<=v;i++) G[i][i]=0.0;
for(int i=1;i<=n;i++)cin>>c[i];
for(int i=1;i<=n;i++)cin>>d[i];
for(int i=1;i<=n;i++)cin>>p[i];
p[0]=1.0;//这句话可有可无
int x,y;
double w;
for(int i=1;i<=e;i++)cin>>x>>y>>w,G[x][y]=min(G[x][y],w),G[y][x]=G[x][y];
for(int k=1;k<=v;k++) for(int i=1;i<=v;i++) for(int j=1;j<=v;j++) G[i][j]=min(G[i][j],G[i][k]+G[k][j]);
f[0][0][0]=0.0;
//f[i][j][0]表示前i节课,申请j节课,第i节课没有申请的最小期望
//f[i][j][1]表示前i节课,申请j节课,第i节课申请的最小期望
// cout<<G[1][2]<<G[1][3]<<G[2][3]<<endl;
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++){
double &ans1=f[i][j][0],&ans2=f[i][j][1];
ans1=min(ans1,f[i-1][j][0]+G[c[i-1]][c[i]]); //第i-1节课没有申请
if(j)ans1=min(ans1,(f[i-1][j][1]+G[c[i-1]][c[i]])*(1.0-p[i-1])+(f[i-1][j][1]+G[d[i-1]][c[i]])*p[i-1]); //第i-1节课申请 申请失败+申请成功
if(j)ans2=min(ans2,(f[i-1][j-1][0]+G[c[i-1]][c[i]])*(1.0-p[i])+(f[i-1][j-1][0]+G[c[i-1]][d[i]])*p[i]); //第i-1节课没有申请 第i节课申请失败+申请成功
if(j>=2)ans2=min(ans2,(f[i-1][j-1][1]+G[c[i-1]][c[i]])*(1.0-p[i-1])*(1.0-p[i])+(f[i-1][j-1][1]+G[d[i-1]][d[i]])*p[i-1]*p[i]+(f[i-1][j-1][1]+G[c[i-1]][d[i]])*(1.0-p[i-1])*p[i]+(f[i-1][j-1][1]+G[d[i-1]][c[i]])*p[i-1]*(1.0-p[i])); //第i-1节课申请 都失败+都成功+一个成功一个失败
}
}
double ans=10000000.0;
for(int i=0;i<=m;i++) ans=min(ans,f[n][i][0]),ans=min(ans,f[n][i][1]);
printf("%.2lf",ans);
return 0;
}
#endif
#ifdef method_2
/*
*/
#endif
#ifdef method_3
/*
*/
#endif
3B16 魔法珠
首先每一堆魔法珠都相互独立且是一个公平组合游戏,所以将每一堆魔法珠视为一个有向图游戏,dfs求其SG函数,用f[x]记忆化搜索,用一个全局变量num和v数组记录当前层面的SG函数,最后用一个空循环作为mex计算即可。
代码如下
/*
*/
#define method_1
#ifdef method_1
/*
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
const int maxn=100+5;
const int maxa=1000+5;
const int INF=0x3f3f3f3f;
int f[maxa],num=0,ans,temp,v[maxa],n;
int SG(int x){
if(f[x]!=-1) return f[x];//记忆化搜索
int now=++num,p=0;
for(int i=1;i*i<=x;i++){ //枚举所有约数 即枚举所有有向图游戏的后继状态 包括1但不包括x
if(x%i==0){
if(i<x) p^=SG(i);
if(i*i!=x&&i>1&&i<x) p^=SG(x/i);
}
}
for(int i=1;i*i<=x;i++){ //枚举删去哪一堆
if(x%i==0){
if(i<x) v[p^f[i]]=now;
if(i*i!=x&&i>1&&i<x) v[p^f[x/i]]=now;
}
}
int pos=0;
for(;v[pos]==now;pos++);//mex操作
return f[x]=pos;
}
int main() {
ios::sync_with_stdio(false);
//freopen("魔法珠.in","r",stdin);
while(cin>>n){
memset(f,-1,sizeof(f)),ans=0;
for(int i=1;i<=n;i++) cin>>temp,ans^=SG(temp);
if(ans) cout<<"freda"<<endl;
else cout<<"rainbow"<<endl;
}
return 0;
}
#endif
#ifdef method_2
/*
*/
#endif
#ifdef method_3
/*
*/
#endif
POJ1704 Georgia and Bob
将棋子两两结对考虑,将两个棋子之间的空格作为nim博弈中一堆石子的个数,全部异或起来就是答案。
解释为什么这么做:最终局面显然是n个棋子位于1,2,3...n的位置,这时每对棋子间距为0(事实上,当一人面对所有棋子中每对棋子间距为0时,他已经处于必输局面)。
对于移动棋子的操作,可以等效于以下情况
图片.png
移动每对棋子中右边的棋子,相当于从石子堆中取走石子。
图片.png
移动每对棋子中左边的棋子,虽然相当于从石子堆中增添石子,但是另一个人都可以对右边的棋子用同样的操作回到原来的个数。
PS:当棋子为奇数的时候,增添一个棋子,位于位置0即可。
图片.png
代码如下
/*
*/
#define method_1
#ifdef method_1
/*
挑战p324
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
const int maxn=1000+5;
const int INF=0x3f3f3f3f;
int a[maxn],n,t;
int main() {
ios::sync_with_stdio(false);
//freopen("Georgia and Bob.in","r",stdin);
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
if(n&1) a[++n]=0;
sort(a+1,a+n+1);
int ans=0;
for(int i=1;i<n;i+=2) ans^=(a[i+1]-a[i]-1);
if(ans) cout<<"Georgia will win"<<endl;
else cout<<"Bob will win"<<endl;
}
return 0;
}
#endif
#ifdef method_2
/*
*/
#endif
#ifdef method_3
/*
*/
#endif









网友评论