前言
虽然忙碌了几周,但是总算院赛完美落幕了。比赛中没出现大的差错,哈哈!!
题解
A.变态的青蛙
设:dp[i]为跳上i级台阶总共的跳法。 那么dp[i] = dp[1] + dp[2] + ... + dp[i-1] + 1 那么很容易推出dp[i]的规律:
dp[i]=2i−1(dp[0]=0)
又因为题目n很大,所以要使用快速幂!
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1000000007;
ll n;
ll p(ll a, ll b)
{
if(b == 0) return 1;
if(b%2) return a*p(a*a%mod,b/2)%mod;
return p(a*a%mod,b/2);
}
int main()
{
while(scanf("%lld",&n)!=EOF)
{
if(n == 0) printf("0\n");
else
printf("%lld\n",p(2,n-1));
}
return 0;
}
B.最大的数
并查集处理交换语句,最后可以得到若干个块。 这样,每个块内元素都可以互相交换。 那么利用贪心策略:让每个块内元素降序排列即可。
代码:
#include <bits/stdc++.h>
using namespace std;
int f[100010];
int n,m;
char str[100010];
vector<int> e[100010];
int pos[100010];
void init()
{
for(int i = 1 ; i <= n ; i ++)
e[i].clear();
for(int i = 1 ; i <= n ; i ++)
f[i] = i;
memset(pos,0,sizeof(pos));
}
int fin(int x)
{
return f[x] == x ? x : f[x] = fin(f[x]);
}
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
scanf("%s",str);
init();
int u,v;
for(int i = 0 ; i < m ; i ++)
{
scanf("%d%d",&u,&v);
int x = fin(u);
int y = fin(v);
if(x != y) f[x] = y;
}
for(int i = 1; i <= n ; i ++)
{
int x = fin(i);
e[x].push_back(str[i-1]-'0');
}
for(int i = 1 ; i <= n ; i ++)
if(e[i].size())
sort(e[i].begin(),e[i].end(),cmp);
for(int i = 1 ; i <= n ; i ++)
{
int x = fin(i);
printf("%d",e[x][pos[x]]);
pos[x] ++;
}
printf("\n");
}
return 0;
}
C.小乐的军事游戏
补出三角形的外接圆,算出三个顶点和圆心连线的夹角。 注意:因为存在钝角的缘故,所以第三个角要用2π减去前面两个角来算。 最后求,三个夹角的最大公约数就行了。 题目中边数不会超过100,精度eps = 1e-2即可。
代码:
#include <bits/stdc++.h>
using namespace std;
#define PI acos(-1)
#define eps 1e-2
double fgcd(double a,double b)
{
return fabs(a) < eps? b : fgcd(fmod(b,a),a);
}
int main() {
double x1,y1,x2,y2,x3,y3;
double p,r,s,k,a,b,c;
double A,B,C;
while(scanf("%lf %lf %lf %lf %lf %lf", &x1, &y1, &x2, &y2, &x3, &y3)!=EOF)
{
a = sqrt( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) ) ;
b = sqrt( (x2-x3)*(x2-x3) + (y2-y3)*(y2-y3) ) ;
c = sqrt( (x1-x3)*(x1-x3) + (y1-y3)*(y1-y3) ) ;
p = ( a + b + c ) / 2.0 ;
s = sqrt( p * (p-a) * (p-b) * (p-c) ) ;
r = a * b * c / ( 4 * s ) ;
if( a > c )
{
k = a ; a = c ; c = k ;
}
if( b > c )
{
k = b ; b = c ; c = k ;
}
A = 2 * asin(a/(2*r)) ;
B = 2 * asin(b/(2*r)) ;
C = 2 * PI - A - B ;
p = fgcd(A, B);
p = fgcd(p, C);
printf("%d\n", (int)(2*PI/p) ) ;
}
return 0;
}
D.紧急救援
1~n号城市的距离可以直接跑一遍以1号为源点的Dijstra即可。 2~n号城市的距离只需要反向建边,然后再跑一遍以1为源点的Dijstra即可。
代码:
#include <bits/stdc++.h>
using namespace std;
int mpt1[1010][1010];
int mpt2[1010][1010];
int d1[1010];
int d2[1010];
int n,m;
void init()
{
memset(mpt1,0x3f3f3f3f,sizeof(mpt1));
memset(mpt2,0x3f3f3f3f,sizeof(mpt2));
for(int i = 1 ; i <= n ; i ++)
mpt1[i][i] = mpt2[i][i] = 0;
}
void dij1()
{
int vis[1010] = {0};
memset(d1,0x3f3f3f3f,sizeof(d1));
d1[1] = 0;
for(int i = 1 ; i <= n ; i ++)
{
int minv = 0x3f3f3f3f,minp = -1;
for(int j = 1 ; j <= n ;j ++)
{
if(vis[j]) continue;
if(d1[j] < minv)
{
minv = d1[j];
minp = j;
}
}
if(minp == -1) break;
vis[minp] = 1;
for(int j = 1 ; j <= n ;j ++)
{
if(vis[j])continue;
if(minv + mpt1[minp][j] < d1[j] ) d1[j] = minv + mpt1[minp][j];
}
}
}
void dij2()
{
int vis[1010] = {0};
memset(d2,0x3f3f3f3f,sizeof(d2));
d2[1] = 0;
for(int i = 1 ; i <= n ; i ++)
{
int minv = 0x3f3f3f3f,minp = -1;
for(int j = 1 ; j <= n ;j ++)
{
if(vis[j]) continue;
if(d2[j] < minv)
{
minv = d2[j];
minp = j;
}
}
if(minp == -1) break;
vis[minp] = 1;
for(int j = 1 ; j <= n ;j ++)
{
if(vis[j])continue;
if(minv + mpt2[minp][j] < d2[j] ) d2[j] = minv + mpt2[minp][j];
}
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
init();
for(int i = 0 ; i < m ; i ++)
{
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
if(mpt1[u][v] > c) mpt1[u][v] = c;
if(mpt2[v][u] > c) mpt2[v][u] = c;
}
dij1();dij2();
int sum = 0 ;
for(int i = 1 ; i <= n ; i ++)
sum += d1[i]+d2[i];
printf("%d\n",sum);
}
return 0;
}
E.寻宝高手
首先一趟bfs就可以处理出海域上所有大陆的大小,并且对不同的大陆进行标号。 然后枚举所有海水点,对于每个海水点O(1)可以得到连接形成陆地的大小。 然后计数更新即可。
代码:
#include <bits/stdc++.h>
using namespace std;
int mpt[110][110];
int n,m;
int s[100010];
int siz[110][110];
int vis[110][110];
int ans[110][110];
int dir[4][2] = {1,0,0,1,-1,0,0,-1};
int maxV,maxC,maxX,maxY;
void dfs(int x,int y,int index)
{
for(int i = 0 ; i < 4 ; i ++)
{
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if(tx < 0 || tx >= n || ty < 0 || ty >= m || vis[tx][ty] || mpt[tx][ty] == 0) continue;
vis[tx][ty] = 1;
mpt[tx][ty] = index;
dfs(tx,ty,index);
}
}
struct node
{
int type,value;
node(int t = 0,int v = 0):type(t),value(v){}
};
bool cmp(node a,node b)
{
return a.value > b.value;
}
void check(int x,int y)
{
vector<node> vec;
for(int i = 0 ; i < 4 ; i ++)
{
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if(tx < 0 || tx >= n || ty < 0 || ty >= m || mpt[tx][ty] == 0)continue;
vec.push_back(node(mpt[tx][ty],siz[tx][ty]));
}
int sum = 0;
vector<int> type;
for(int i = 0 ; i < vec.size() ; i ++)
{
bool flag = true;
for(int j = 0 ; j < type.size() ; j ++)
{
if(vec[i].type == type[j]) flag = false;
}
if(flag)
{
sum += vec[i].value;
type.push_back(vec[i].type);
}
}
ans[x][y] = sum;
if(ans[x][y] > maxV)
{
maxC = 1;
maxV = ans[x][y];
maxX = x;
maxY = y;
}
else if(ans[x][y] == maxV)
{
maxC ++;
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i = 0 ; i < n ; i ++)
{
for(int j = 0 ; j < m ; j ++)
{
scanf("%d",&mpt[i][j]);
}
}
int index = 2;
for(int i = 0 ; i < n ; i ++)
{
for(int j = 0 ; j < m ; j ++)
{
if(mpt[i][j] == 1)
{
memset(vis,0,sizeof(vis));
vis[i][j] = 1;
mpt[i][j] = index;
dfs(i,j,index);
index ++;
}
}
}
memset(s,0,sizeof(s));
for(int i = 0 ; i < n ; i ++)
{
for(int j = 0 ; j < m ; j ++)
{
if(mpt[i][j] != 0)
{
++s[mpt[i][j]];
}
}
}
for(int i = 0 ; i < n ; i ++)
{
for(int j = 0 ; j < m ; j ++)
{
if(mpt[i][j] != 0)
{
siz[i][j] = s[mpt[i][j]];
}
}
}
maxV = -1;
maxC = 0;
memset(ans,0,sizeof(ans));
for(int i = 0 ; i < n ; i ++)
{
for(int j = 0 ; j < m ; j ++)
{
if(mpt[i][j] == 0)
check(i,j);
}
}
if(maxC == 1) printf("%d %d\n",maxX+1,maxY+1);
else printf("No\n");
}
return 0;
}
F.XX-XY
因为长度1-3的时候我们很容易得打答案。 我们考虑长度n>=4的情况: 我们将字符串结尾的三个字符状态分为5类:
0:XYZdp[i][0]=dp[i−1][0]+2dp[i−1][1]+2dp[i−1][2]
1:XXYdp[i][1]=3dp[i−1][3]+3dp[i−1][4]
2:XYXdp[i][2]=dp[i−1][0]+dp[i−1][1]+dp[i−1][2]
3:YXXdp[i][3]=dp[i−1][0]+dp[i−1][1]+dp[i−1][2]
4:XXXdp[i][4]=dp[i−1][3]+dp[i−1][4]因为所求长度n过大,不能直接递推,所以要使用快速矩阵幂。
代码:
#include <bits/stdc++.h>
using namespace std;
const int mod = 5120141035;
const int MAX = 6;
typedef struct{
long long m[MAX][MAX];
} Matrix;
Matrix P = {1,0,1,1,0,1,
2,0,1,1,0,1,
2,0,1,1,0,1,
0,3,0,0,1,1,
0,3,0,0,1,1,
0,0,0,0,0,0
};
Matrix I = {1,0,0,0,0,0,
0,1,0,0,0,0,
0,0,1,0,0,0,
0,0,0,1,0,0,
0,0,0,0,1,0,
0,0,0,0,0,1
};
Matrix matrixmul(Matrix a,Matrix b) //¾ØÕó³Ë·¨
{
int i,j,k;
Matrix c;
for (i = 0 ; i < MAX; i++)
for (j = 0; j < MAX;j++)
{
c.m[i][j] = 0;
for (k = 0; k < MAX; k++)
c.m[i][j] += (a.m[i][k] * b.m[k][j])%mod;
c.m[i][j] %= mod;
}
return c;
}
Matrix quickpow(long long n)
{
Matrix m = P, b = I;
while (n >= 1)
{
if (n & 1)
b = matrixmul(b,m);
n = n >> 1;
m = matrixmul(m,m);
}
return b;
}
int main()
{
int now = clock();
long long n;
while(scanf("%lld",&n)!=EOF)
{
if(n == 1) printf("4\n");
else if(n == 2) printf("16\n");
else if(n == 3) printf("64\n");
else{
Matrix mt = quickpow(n-2);
printf("%lld\n",(mt.m[0][5]*24+mt.m[1][5]*12+mt.m[2][5]*12+mt.m[3][5]*12+mt.m[4][5]*4)%mod);
}
}
return 0;
}
G.进程调度
前置任务 使用 拓扑排序 处理 优先级 使用 优先队列 处理 值得注意的是: 这里因为要有限考虑优先度最高的,所以顺向优先队列的选择是局部的。 所以这要反向建边,然后优先度最低的先出,然后反向输出。
代码:
#include <bits/stdc++.h>
using namespace std;
int pre[200010];
vector<int> aft[200010];
int n,m;
int ans[200010],top = 0;
priority_queue<int> team;
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(pre,0,sizeof(pre));
top = 0;
while(team.size())team.pop();
for(int i = 0 ; i <= n ; i ++) aft[i].clear();
for(int i = 0 ; i < m ; i ++)
{
int a,b;
scanf("%d%d",&a,&b);
pre[a] ++;
aft[b].push_back(a);
}
for(int i = 1 ; i <= n ; i ++)
if(pre[i] == 0) team.push(i);
while(team.size())
{
int now = team.top();
team.pop();
ans[top++] = now;
for(int i = 0 ; i < aft[now].size() ; i ++)
{
pre[aft[now][i]] --;
if(pre[aft[now][i]] == 0) team.push(aft[now][i]);
}
}
for(int i = top - 1 ; i >= 1 ; i --)
{
printf("%d ",ans[i]);
}
printf("%d\n",ans[0]);
}
return 0;
}
- H.LOL
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
ll inv(ll a)
{
if(a == 1) return 1;
return inv(mod%a)*(mod-mod/a)%mod;
}
ll pre[2000010]={0,1};
ll C(ll a,ll b)
{
if(a == b) return 1;
if(a == 0) return 1;
return pre[b]*inv(pre[a])%mod*inv(pre[b-a])%mod;
}
void init()
{
for(int i = 2 ; i < 2000010 ; i ++)
pre[i] = (i*pre[i-1])%mod;
}
int main()
{
ll n,m,k;
init();
while(scanf("%lld %lld %lld",&n,&m,&k)!=EOF)
{
if(k == 0)
{
printf("0\n");
continue;
}
ll step = C(k-1,m+n-1);
step = step*n%mod;
step = step*((n*k-k+m)%mod)%mod;
step = step*inv(m+n-1)%mod;
printf("%lld\n",step);
}
return 0;
}
I.A Simple Problem
题意就是:给你两个数n、m,从小到大求第m个与n互质的数。 做法: 二分答案,那么这个题目就变成了求[1,x]中与n互质的数的个数。 筛选出n的质因子个数,然后用容斥定理可以得到。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int fac[20];
int book[10000];
int tot;
ll Count(ll n, int m) {
ll g = 0, sum = n;
book[++g] = 1;
for(int i = 1; i <= tot; i ++){
int t = g;
for(int j = 1; j <= g; ++j){
book[++t] = book[j] * fac[i] * -1;
sum += n / book[t];
}
g = t;
}
return sum;
}
ll solve(int n, int m){
ll l = 1, r = 10000000000, mid;
while(l <= r){
mid = (l + r) >> 1;
if(Count(mid, n) >= m) r = mid - 1;
else l = mid + 1;
}
return l;
}
void init(int n) {
int k = 0;
for(int i = 2; i * i <= n; ++i){
if(n % i == 0) fac[++k] = i;
while(n % i == 0) n /= i;
}
if(n > 1) fac[++k] = n;
tot = k;
}
int main()
{
int n, m;
while(scanf("%d%d", &n, &m) != EOF)
{
init(n);
ll ans = solve(n, m);
printf("%lld\n", ans);
}
return 0;
}
J.字符串
签到题,开个数组标记一下,遇到出现过的就不输出。
代码:
#include <bits/stdc++.h>
using namespace std;
char a[100010];
char b[100010];
int zz[1000];
int main()
{
while(scanf("%s%s",a,b)!=EOF)
{
int na = strlen(a);
int nb = strlen(b);
memset(zz,0,sizeof(zz));
for(int i = 0 ; i < na ; i ++)
if(zz[a[i]] == 0)
{
printf("%c",a[i]);
zz[a[i]]++;
}
for(int i = 0 ; i < nb ; i ++)
if(zz[b[i]] == 0)
{
printf("%c",b[i]);
zz[b[i]]++;
}
printf("\n");
}
return 0;
}