这题是送分题,主要考察转义字符
#include
using namespace std;
int main(){
int n;
cin>>n;
while(n--){
cout<<"\"www.lpoj.cn is the best!\""<
考察int的范围,这题要用long long int长整型才能保存答案
#include
using namespace std;
int main()
{
int n,i;
long long int tmp,s=0;
cin >>n;
for (i=0;i>tmp;
if (tmp%2==0) { s+=tmp; }
}
cout <
主要考察大家的读题和细心能力。这道题用printf的话,会简单很多,少很多逻辑。
#include
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;
}
这题是出题人拍脑袋想出来的。一个for循环就好了。
#include
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
简单思维题,数字优先拆3,接着拆2,至于为什么,因为要使乘积最大,肯定是越平均越好,直到3,因为3不能拆了。
#include
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;
}
做法就是因为数据范围很小,所以我们可以枚举所有直线,然后计算所有圆到直线的距离即可。但是这里有很多特殊情况要考虑。比如 斜率不存在的情况等等。
#include
#include
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)>n;
for(int i=0;i
#include
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;
}
首先因为行内可以随意变换,所以只要直接统计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
using namespace std;
int n, m;
vector 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(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;
}
暴力:可以发现这个通项是两项线性递推的公式,double暴力算出前5项,暴力枚举两项系数即可,也可以解二元一次方程组。
科学做法:用特征根来求解,可以百度特征方程求解斐波那契通项
#include
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;
}
概率动态规划,令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
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
引入某位集训队大佬的题解:
输入一棵树,求树上所有两点之间路径或之和。
权神设计出来是用并查集做的。
然而第一反应就是向上合并维护二进制位。写了一发维护1的个数愉快的wa了,之后就忘了有这题了。最近这题被大师兄秒掉了,被教育了一手可以先当成整棵树全是1来做,然后再合并0的个数,减掉0的情况。
瞬间好写了很多。。。。
我们把子树0的个数向上合并的时候,检查一下父节点这一位是否是0,如果是0才合并,因为父节点是所以子节点的必经之路,如果父节点是1,那么子节点经过父节点往上或出来这一位就是1了。
#include
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 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;
}
初始化部分,对两棵树分别做深搜,维护好LCA(最近公共祖先)所需信息,查一棵树上任意两点间的距离。
看询问,虽然说是两个人的相遇问题,但由于也是两个人的距离之和,所以可以看成一个点到另一个点的最短距离。
传送门只有M个,那么在每个询问中,我们只需要关注这M对点的路径。
比赛时有同学已经做到了LCA了,只是他是通过枚举M对点到两个点的距离之和求最小值,也就是说,在这个策略里,传送门只利用一次。
但是实际上,传送门可以多次利用,在两棵树之间来回跳来求出更短的距离,所以这里的子问题应该是,起点到这2M个点的最短路径,跑个单源最短路算法即可。
#include
using namespace std;
const int N = 2e5 + 10;
const int L = 20;
const int M = 8;
typedef long long LL;
typedef pair 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 V[N];
void dfs(int x){
for(int i=0; i 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 Q;
Node nd;
LL g;
for(int i=0; i= ans) break;
ans = min(ans, nd.dis + cal(a[nd.id]+n, y));
done[nd.id] = 1;
for(int i=0; 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