题目连接 :https://oj.lpoj.cn/contest/56
A lpoj is the best
这题是送分题,主要考察转义字符
#include<iostream>
using namespace std;
int main(){
int n;
cin>>n;
while(n--){
cout<<"\"www.lpoj.cn is the best!\""<<endl;
}
return 0;
}
B 偶数求和
考察int的范围,这题要用long long int长整型才能保存答案
#include<iostream>
using namespace std;
int main()
{
int n,i;
long long int tmp,s=0;
cin >>n;
for (i=0;i<n;i++)
{
cin >>tmp;
if (tmp%2==0) { s+=tmp; }
}
cout <<s;
return 0;
}
C 出题人的良心
主要考察大家的读题和细心能力。这道题用printf的话,会简单很多,少很多逻辑。
#include <stdio.h>
int main()
{
int a,b;
float c;
scanf("%d %d",&a,&b);
c=(float)a/b;
if(b>0)
printf("%d/%d=%.2f\n",a,b,c);
if(b<0)
printf("%d/(%d)=%.2f\n",a,b,c);
if(b==0)
printf("%d/%d=Error\n",a,b);
return 0;
}
D cat & grep
这题是出题人拍脑袋想出来的。一个for循环就好了。
#include<bits/stdc++.h>
using namespace std;
int n;
char s[1100];
int main(){
while(~scanf("%s", s)){
n = strlen(s);
int ans = 0;
for(int i=3; i<n; i++){
if(s[i-3] == 'l' && s[i-2] == 'p' && s[i-1] == 'o' && s[i] == 'j'){
ans++;
}
}
printf("%d\n", ans);
}
return 0;
}
E ka ge bu n shi n no jyu tsu
简单思维题,数字优先拆3,接着拆2,至于为什么,因为要使乘积最大,肯定是越平均越好,直到3,因为3不能拆了。
#include<bits/stdc++.h>
using namespace std;
int n, m;
bool ok(){
if(n >= 60) return 1;
long long s = 1;
while(n > 4){
s = s * 3;
n -= 3;
if(s >= m) return 1;
}
return s * n >= m;
}
int main(){
while(~scanf("%d %d", &n, &m)){
puts(ok()?"Yes":"No");
}
return 0;
}
F 好快的刀
做法就是因为数据范围很小,所以我们可以枚举所有直线,然后计算所有圆到直线的距离即可。但是这里有很多特殊情况要考虑。比如 斜率不存在的情况等等。
#include <iostream>
#include <cmath>
using namespace std;
const double eps=1e-8;
const double inf =1e20;
//const double pi=acos(-1.0);
int sgn(double x){
if(fabs(x)<eps) return 0;
if(x<0) return -1;
else return 1;
}
struct Point{
double x,y;
Point(){}
Point(double _x,double _y){
x=_x;
y=_y;
}
void input(){
scanf("%lf%lf",&x,&y);
}
bool operator ==(Point b)const{
return sgn(x-b.x)==0&&sgn(y-b.y)==0;
}
Point operator -(const Point &b)const{
return Point(x-b.x,y-b.y);
}
double operator ^(const Point &b)const{
return x*b.y-y*b.x;
}
double distance(Point p){
return hypot(x-p.x,y-p.y);
}
};
struct Line{
Point s,e;
void input(){
s.input();
e.input();
}
double length(){
return s.distance(e);
}
double dispointtoline(Point p){
return fabs((p-s)^(e-s))/length();
}
};
struct circle{
Point p;
double r;
void input(){
p.input();
scanf("%lf",&r);
}
bool operator ==(circle v){
return (p==v.p);
}
int relationline(Line v){
double dst=v.dispointtoline(p);
if(sgn(dst-r)<0) return 2;
else if(sgn(dst-r)==0) return 1;
else return 0;
}
};
int main(){
int n,ans(2),cou,flag(0);
circle cir[105];
Line line1;
cin>>n;
for(int i=0;i<n;i++) cir[i].input();
for(int i=0;i<n;i++){
if(cir[i]==cir[0]&&i==n-1)flag=1;
}
if(flag){
cout<<n<<'\n';
return 0;
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cou=2;
if(i==j) continue;
line1.e=cir[i].p;
line1.s=cir[j].p;
for(int k=0;k<n;k++){
if(k==i||k==j) continue;
if(cir[k].relationline(line1)!=0) cou++;
}
ans=max(ans,cou);
}
}
cout<<ans<<'\n';
return 0;
}
G 沙雕
#include <bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
int n,m,w,h;
int p1,p2;
int main(){
cin >> n >> m >> w >> h;
for (int i=w+2;i<=n;i++)
p1=(p1+1ll*(i-w-1)*(n-i+1)%mod)%mod;
for (int i=h+2;i<=m;i++)
p2=(p2+1ll*(i-h-1)*(m-i+1)%mod)%mod;
cout << 1ll*p1*p2%mod;
}
H 完美子矩阵
首先因为行内可以随意变换,所以只要直接统计0-9数字的每一行是否符合回文的条件。然后,N * M就压缩成N * 10的东西了。因为要满足行回文,因此只要每一行0-9对应的数量一样,并且每一行都合法就是一个回文子矩阵。因为,问题就转化为给你一个序列,问你回文的子串有多少个。
O(N^3)做法:O( n^2)枚举左右端点,暴力O(n)检测,这个明显时间过不了
O(N^2)做法:O(n)枚举回文的对称点,O(n)检测该回文对称的最长半径
O(N)做法:直接用Manacher算法
#include <bits/stdc++.h>
using namespace std;
int n, m;
vector<int> num[2005];
bool ok[2005];
bool check1(int r)
{
int sum = 0;
for (auto &&i:num[r])
if (i & 1)
++sum;
return sum < 2;
}
int main()
{
string t;
cin >> n >> m;
for (int i = 0; i < n; ++i)
num[i] = vector<int>(10);
for (int i = 0; i < n; i++)
{
cin >> t;
for (auto &&j:t)
++num[i][j - 0x30];
ok[i] = check1(i);
}
int l, r, ans = 0;
for (int i = 0; i < n; ++i)
{
l = r = i;
while (l >= 0 && r < n)
{
if (ok[l] && ok[r] && num[l] == num[r])
++ans;
else
break;
--l, ++r;
}
l = i, r = i + 1;
while (l >= 0 && r < n)
{
if (ok[l] && ok[r] && num[l] == num[r])
++ans;
else
break;
--l, ++r;
}
}
cout << ans;
return 0;
}
I 斐波那契通项
暴力:可以发现这个通项是两项线性递推的公式,double暴力算出前5项,暴力枚举两项系数即可,也可以解二元一次方程组。
科学做法:用特征根来求解,可以百度特征方程求解斐波那契通项
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int n, mod;
LL a, b, F[N];
int main(){
scanf("%lld %lld %d %d", &a, &b, &n, &mod);
LL A = (a * 2) % mod;
LL B = ((b - a * a) % mod + mod) % mod;
F[0] = 2;
F[1] = A;
for(int i=2; i<=n; i++){
F[i] = (F[i-1] * A + F[i-2] * B) % mod;
}
printf("%lld\n", F[n]);
return 0;
}
J 摇摇摇
概率动态规划,令fi表示在i时走到1的概率期望,其中f1=0,通过解方程,f2=f3=f4=f5=f6=6,接下来就是一个线性递推fi = 1/6*(fi-1+fi-2+fi-3+fi-4+fi-5+fi*6)+1。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n;
double dp[N];
int main(){
dp[1] = 0.0;
for(int i=2; i<=6; i++){
dp[i] = 6.0;
}
for(int i=7; i<N; i++){
dp[i] = 1.0;
for(int j=1; j<=6; j++){
dp[i] = dp[i] + dp[i-j] / 6.0;
}
}
while(~scanf("%d", &n)){
printf("%.3f\n", dp[n]);
}
return 0;
}
K Hongrock的柠檬树
引入某位集训队大佬的题解:
输入一棵树,求树上所有两点之间路径或之和。
权神设计出来是用并查集做的。
然而第一反应就是向上合并维护二进制位。写了一发维护1的个数愉快的wa了,之后就忘了有这题了。最近这题被大师兄秒掉了,被教育了一手可以先当成整棵树全是1来做,然后再合并0的个数,减掉0的情况。
瞬间好写了很多。。。。
我们把子树0的个数向上合并的时候,检查一下父节点这一位是否是0,如果是0才合并,因为父节点是所以子节点的必经之路,如果父节点是1,那么子节点经过父节点往上或出来这一位就是1了。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
typedef long long ll;
ll ans;
int n, val[maxn];
int p[20], sz[maxn][20];
vector<int> G[maxn];
void crack(int x) {
for (int i = 0; i < 20; ++i) {
sz[x][i] = !(val[x] >> i & 1);
}
}
void dfs(int x, int fa) {
crack(x);
for (auto v:G[x]) {
if (v == fa) {
continue;
}
dfs(v, x);
for (int i = 0; i < 20; ++i) {
ans -= 1LL * p[i] * sz[x][i] * sz[v][i];
if (!(val[x] >> i & 1)) {
sz[x][i] += sz[v][i];
}
}
}
}
int main() {
ios::sync_with_stdio(0);
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> val[i];
}
int u, v;
for (int i = 1; i < n; ++i) {
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
p[0] = 1;
for (int i = 1; i < 20; ++i) {
p[i] = p[i - 1] << 1;
}
for (int i = 0; i < 20; ++i) {
ans += 1LL * p[i] * n * (n - 1) / 2;
}
dfs(1, 0);
cout << ans;
return 0;
}
L Alice and Bob
初始化部分,对两棵树分别做深搜,维护好LCA(最近公共祖先)所需信息,查一棵树上任意两点间的距离。
看询问,虽然说是两个人的相遇问题,但由于也是两个人的距离之和,所以可以看成一个点到另一个点的最短距离。
传送门只有M个,那么在每个询问中,我们只需要关注这M对点的路径。
比赛时有同学已经做到了LCA了,只是他是通过枚举M对点到两个点的距离之和求最小值,也就是说,在这个策略里,传送门只利用一次。
但是实际上,传送门可以多次利用,在两棵树之间来回跳来求出更短的距离,所以这里的子问题应该是,起点到这2M个点的最短路径,跑个单源最短路算法即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
const int L = 20;
const int M = 8;
typedef long long LL;
typedef pair<int,int> pii;
const LL inf = 1e18;
#define pb push_back
#define mp make_pair
int n, Q, m, a[M], dep[N], fa[N][L];
LL dist[N];
vector<pii> V[N];
void dfs(int x){
for(int i=0; i<V[x].size(); i++){
int j = V[x][i].first;
if(j == fa[x][0]) continue;
dist[j] = dist[x] + V[x][i].second;
dep[j] = dep[x] + 1;
fa[j][0] = x;
dfs(j);
}
}
void init(){
dep[1] = 0;
fa[1][0] = -1;
dist[1] = 0;
dfs(1);
dep[n+1] = 0;
fa[n+1][0] = -1;
dist[n+1] = 0;
dfs(n+1);
for(int j=1; j<L; j++){
for(int i=1; i<=n*2; i++){
if(fa[i][j-1] == -1){
fa[i][j] = fa[i][j-1];
} else {
fa[i][j] = fa[fa[i][j-1]][j-1];
}
}
}
}
bool done[M];
LL d[M];
struct Node{
int id;
LL dis;
Node(){}
Node(int id, LL dis):id(id),dis(dis){}
bool operator < (const Node &A)const{
return dis > A.dis;
}
};
int lca(int u, int v){
if(dep[u] > dep[v]) swap(u, v);
for(int i=L-1; i>=0; i--){
if(dep[v] - dep[u] >> i & 1){
v = fa[v][i];
}
}
if(u == v) return u;
for(int i=L-1; i>=0; i--){
if(fa[u][i] != fa[v][i]){
u = fa[u][i];
v = fa[v][i];
}
}
return fa[u][0];
}
LL cal(int x, int y){
int u = lca(x, y);
return dist[x] + dist[y] - dist[u] * 2;
}
LL solve(int x, int y, int m){
LL ans = inf;
priority_queue<Node> Q;
Node nd;
LL g;
for(int i=0; i<m; i++){
d[i] = cal(x, a[i]);
Q.push(Node(i, d[i]));
done[i] = 0;
}
while(!Q.empty()){
nd = Q.top(); Q.pop();
if(done[nd.id]) continue;
if(nd.dis >= ans) break;
ans = min(ans, nd.dis + cal(a[nd.id]+n, y));
done[nd.id] = 1;
for(int i=0; i<m; i++){
if(done[i]) continue;
g = min(cal(a[i], a[nd.id]), cal(a[i]+n, a[nd.id]+n));
if(d[i] > nd.dis + g){
d[i] = nd.dis + g;
Q.push(Node(i, d[i]));
}
}
}
return ans;
}
int main(){
int x, y, z, m;
scanf("%d", &n);
for(int i=1; i<n; i++){
scanf("%d %d %d", &x, &y, &z);
V[x].pb(mp(y, z));
V[y].pb(mp(x, z));
}
for(int i=1; i<n; i++){
scanf("%d %d %d", &x, &y, &z);
V[x].pb(mp(y, z));
V[y].pb(mp(x, z));
}
init();
scanf("%d", &Q);
while(Q--){
scanf("%d %d %d", &x, &y, &m);
for(int i=0; i<m; i++){
scanf("%d", a+i);
}
printf("%lld\n", solve(x, y, m));
}
return 0;
}