$ \sum{i=0}{n-1}\sum{j=i}{n-1}\mid Ai - Aj \mid $

小学生在上课

题目大意:给你一个正整数N,问你1 ~ (n-1) 所有在模N下的逆的和(只计算存在的)。

分析:

1、首先,一个数a在模N下存在逆当且仅当 gcd(a, N) = 1

2、易证,不同的数在模N下的逆不同

3、一个数在模N下的逆是它本身

4、因此,令A={与N互质的数} B = {A里每一个数在模N下的逆},易证 A = B

5、所以只需求1-(n-1)中与N互质的数的和

6、因为gcd(a, N)= 1 gcd(N-a, N)

7、因此

时间复杂度为O(sqrt(N))

另:不懂ϕ(N)的自己去查 欧拉函数

#include <stdio.h>
#include <math.h>
int main()
{
int T,n,i;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
long long ans = n;
for(i = 2;i <= sqrt(n);i ++)
{
if(n % i == 0)
{
ans *= i - 1;
n /= i;
while(n % i == 0)
{
ans *= i;
n /= i;
}
}
}
if (n>1)
ans *= n - 1;
ans /= 2;
printf("%lld\n",ans);
}
return 0;
}

小学生森林

每次O(log)的去交换行和列就可以了,然后最后再看看那个位置是什么。

如果没看懂的话,那就仔细看看代码吧~

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<list>
#include<map>
#include<set>
#include<vector>
#include<cstring>
#include<iostream>
#define FOR(i,a,b) for(i=(a);i<=(b);i++)
#define ROF(i,a,b) for(i=(a);i>=(b);i--)
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
map<LL,LL> a;
map<int,int> hang;
map<int,int> lie;
int n,m,K;
LL gp(LL x,LL y)
{
return (LL)x*(3*1e9)+(LL)y;
}
int main()
{
scanf("%d%d%d",&n,&m,&K);
int x,y,c,i,tt,qq,a0,b0;
FOR(i,1,K)
{
scanf("%d%d%d",&x,&y,&c);
LL t=gp(x,y);
if (!a.count(t)) a[t]=c;
else a[t]+=c;
}
scanf("%d",&tt);
while (tt--)
{
scanf("%d%d%d",&qq,&a0,&b0);
if (qq==1)
{
if (!hang.count(a0)) hang[a0]=a0;
if (!hang.count(b0)) hang[b0]=b0;
int t=hang[a0];hang[a0]=hang[b0];hang[b0]=t;
}else if (qq==2)
{
if (!lie.count(a0)) lie[a0]=a0;
if (!lie.count(b0)) lie[b0]=b0;
int t=lie[a0];lie[a0]=lie[b0];lie[b0]=t;
}else
{
if (hang.count(a0)) x=hang[a0];else x=a0;
if (lie.count(b0)) y=lie[b0];else y=b0;
LL t=gp(x,y);
if (!a.count(t)) printf("%lld\n",0);
else printf("%lld\n",a[t]);
}
}
return 0;
}

小学生放假了

这题N^2M的dp应该是显然的……算了还是说下吧,先将Ci从大到小排序。f[i][j]表示前i个小学生还剩下j个商品可以设定价格,可以得到的最多的收入,f[i][j] = max(f[k][j – 1] + Ck * (i – k) | 0 <= k < i),这个dp应该很好理解。

现在的思路很简单,就是把转移优化到O(1),这怎么做呢?

经过观察可以发现,每个f[i][j]的决策g[i][j]在两维上都是单调不减的,即 g[i][j] <= g[i][j + 1],g[i][j] <= g[i + 1][j]。然后就有利用二维决策单调性的O(NM)算法了:对于每个f[i][j]计算决策的时候,只枚举[g[i – 1][j], g[i][j + 1]]之间的决策,可以证明这个复杂度是O(NM)的

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=10005,M=2005;
int n,m,g[N][M];
long long f[N][M],c[N];
int main()
{
#ifndef ONLINE_JUDGE
freopen("ch.in","r",stdin);
freopen("ch.out","w",stdout);
#endif
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
scanf("%d",&c[i]),g[i+1][m+1]=i;
sort(c,c+n);
for(int i=1;i<=n;i++)
for(int j=m;j;j--)
for(int k=g[i-1][j];k<=g[i][j+1]&&k<i;k++)
if(f[i][j]<f[k][j-1]+c[k]*(i-k)){
f[i][j]=f[k][j-1]+c[k]*(i-k);
g[i][j]=k;
}
printf("%lld\n",f[n][m]);
return 0;
}

小学生的游戏

这个题目一看就是矩阵快速幂吧……

算法1:容易发现步长是以M为循环节的。构造M个转移矩阵,先把M个转移矩阵乘起来得到A,再计算A^(N / M),最后再乘上前N % M个转移矩阵,得到答案。这种算法是O(M^4)的,对于M=200的数据还是过不了的……

算法2:上面这种算法把M个转移矩阵乘起来这一步太浪费时间,所以我们考虑直接构造转移M个格子的矩阵A,构造这个矩阵是O(M^3)的,然后再用上面的算法就可以了(前M个格子的数可能要特殊处理)

AC算法:虽然算法2已经达到了O(M^3logN)的复杂度,但由于常数较大还是过不了本题……怎么办呢???只能优化矩阵乘法了。我们发现如果一个矩阵的某些项为0,根本没有必要乘这么多次,于是如果碰到一个值为0的项就直接跳过第三重循环(具体看AC程序)。事实证明,这样优化了大约2~5倍的时间,常数小的程序已经能在1秒内跑出结果了!

4ms 算法:AC这题的都用的是4ms 算法……由于我实在是太弱了,真心不会这种算法啊……

#include <algorithm>
#include <iostream>
#include <iomanip>
#include <complex>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <cstdio>
#include <cmath>
#include <map>
#include <set>
using namespace std;
//#pragma comment(linker,"/STACK:102400000,102400000") long long mod = 1000000009LL;
const int N = 20;
int M;
int mapTo[201]; struct matrix
{
long long x[N+1][N+1];
matrix(){memset(x, 0, sizeof(x));}
matrix(long long init)
{
memset(x, 0, sizeof(x));
for(int i = 0; i <= N; i++)
x[i][i] = init;
}
matrix operator +(matrix that)
{
matrix ret;
for(int i = 0; i <= N; i++)
for(int j = 0; j <= N; j++)
ret.x[i][j] = (x[i][j] + that.x[i][j]) % mod;
return ret;
}
matrix operator -(matrix that)
{
matrix ret;
for(int i = 0; i <= N; i++)
for(int j = 0; j <= N; j++)
ret.x[i][j] = (x[i][j] - that.x[i][j] + mod) % mod;
return ret;
}
matrix operator *(matrix that)
{
matrix ret;
for(int i = 0; i <= N; i++)
for(int j = 0; j <= N; j++)
for(int k = 0; k <= N; k++)
ret.x[i][j] = (ret.x[i][j] + x[i][k] * that.x[k][j]) % mod;
return ret;
}
}I(1); matrix power(matrix b, long long e)
{
matrix ret = I;
while(e)
{
if(e&1) ret = ret * b;
b = b * b;
e /= 2;
}
return ret;
} /* Note
1. Set N: matrix size
2. Set mod
*/ /* Eaxmple
matrix init, trans;
init.x[1][1] = 2, init.x[2][1] = 1;
trans.x[1][1] = 2, trans.x[1][2] = 1, trans.x[2][2] = 2;
cout << (power(trans, 5) * init).x[1][1] << endl;
*/ long long gcd(long long a, long long b)
{
if(a == 0 || b == 0)return a + b;
return gcd(b, a % b);
} matrix op(int i)
{
int a = gcd(i, M);
int b = gcd(i+1, M);
matrix ret = I;
ret.x[0][0] = 0;
ret.x[0][mapTo[b]] = 1;
for(int i = 1; i <= a; i++)
if(a % i == 0)
if(mapTo[i] != -1)
ret.x[mapTo[i]][mapTo[b]] += 1;
return ret;
} matrix trans[201];
int have[201]; int MAIN()
{
long long n, m;
cin >> n >> m;
memset(have, 0, sizeof(have));
for(int i = 1; i <= m; i++)
have[gcd(i, m)] = 1;
int z = 0;
mapTo[0] = 0;
for(int i = 1; i <= m; i++)
if(have[i])
{
z ++;
mapTo[i] = z;
}
else
mapTo[i] = -1;
n --;
M = m;
matrix init;
for(int i = 1; i <= N; i++)
init.x[i][0] = 1;
trans[0] = I;
for(int i = 1; i <= m; i++)
trans[i] = op(i) * trans[i-1];
matrix t = power(trans[m], n/m);
t = trans[n%m] * t;
t = t * init;
cout << t.x[0][0] << endl; return 0;
} int main()
{
#ifdef LOCAL_TEST
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
ios :: sync_with_stdio(false);
cout << fixed << setprecision(16);
return MAIN();
}

5、小学生的旅行

题意大意:给一个N个点,M条边的无向连通图,再给你K个权值,问这些权值中,可以从u走到v时经过的边值都不大于这个权值的有多少个。其中这些权值会修改。

分析:

1、首先,如果从u走到v的所有路径中,最大边最小的边权为l,那么,所有不小于l的权值都符合,所有小于l的权值都不符合。

2、因此,可以把题目分为两步,首先求出从u走到v的所有路径中最大边最小的边权l,再求有多少个权值不小于l。

3、取出原图中的所有点,将边从小到大排序,一次加入这个新图,当使u, v恰好连通时的边权就是要求的L。停止加边。

4、证明:从u走到v,可以只经过不比L大的边,因为新图中u可以走到v,而且图中的所有边权不大于L。去掉边权为L的边,得到的图中u无法到v, 因此不存在L’, 从u走到v,可以只经过不比L’大的边,其中L’<L。

因此L是原图中从u走到v的所有路径中,最大边最小的边权。

5、如果在加边的过程中,不添加在同一连通块中的两个点之间的边,使整个 图连通后,这个图是原图的最小生成树,对任意两点u,v,从u走到v的唯 一路径上的最大边就是原图中从u走到v的所有路径中,最大边最小的边权。 (因为L一定在这条路径上,且这条路径上的其它边都小于L)

6、所以先求出最小生成树,再通过倍增,可以在O(logN)的时间求出从u走到v的所有路径中,最大边最小的边权。

7、至于如何在一组数中查找有多少个,其大小不小于l,还会动态修改这些数的权值,可以用平衡树做,但是考虑到本题中l≤200000,可以用数组a[i]表示权值为i的数有多少个,所有比lmax大的数可以看成lmax,然后用平衡树/树状数组来维护,可以做到O(log(lmax))的查询和修改

总复杂度为O(MlogM + QlogN + Qlog(lmax))

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define pb push_back
using namespace std;
const int W=200100;
struct edge{int x, y, d;}E[W];
int mx[W][18], anc[W][18], uni[W], t[W], dep[W], a[W];
int N, M, K, Q;
vector<int>nxt[W], c[W];
int lowbit(int x){return x & (-x);}
void add(int x, int d)
{
for (; x <= 200001; x += lowbit(x)) a[x] += d;
}
int getsum(int x)
{
int sum=0;
for (; x > 0; x -= lowbit(x)) sum += a[x];
return sum;
}
void Read()
{
scanf("%d%d%d%d", &N, &M, &K, &Q);
for (int i=1; i <= M; i++)
{
scanf("%d%d%d", &E[i].x, &E[i].y, &E[i].d);
E[i].d++;
}
for (int i=1; i <= K; i++)
{
scanf("%d", &t[i]); t[i]++;
if (t[i] >= 200001) t[i]=200001;
add(t[i], 1);
}
}
bool cmp(const edge &A, const edge &B){return A.d < B.d;}
int Uni(int x)
{
if (uni[x] == x) return x;
return uni[x]=Uni(uni[x]);
}
void Init()
{
for (int i=1; i <= N; i++) uni[i]=i;
for (int i=1; i <= M; i++)
{
int x=E[i].x, y=E[i].y;
x=Uni(x); y=Uni(y);
if (x == y) continue;
uni[x]=y;
nxt[E[i].x].pb(E[i].y);
nxt[E[i].y].pb(E[i].x);
c[E[i].x].pb(E[i].d);
c[E[i].y].pb(E[i].d);
}
}
void Build(int x, int fa, int D)
{
dep[x]=D;
anc[x][0]=fa;
for (int i=1; i <= 17; i++)
{
if (dep[x] - (1 << i) < 1) break;
anc[x][i]=anc[anc[x][i - 1]][i - 1];
mx[x][i]=max(mx[x][i - 1], mx[anc[x][i - 1]][i - 1]);
}
for (int i=0; i < (int)nxt[x].size(); i++)
{
int y=nxt[x][i];
if (y == fa) continue;
mx[y][0]=c[x][i];
Build(y, x, D + 1);
}
}
int Find(int u, int v)
{
int sum=0;
if (dep[u] < dep[v]) swap(u, v);
for (int i=17; i >= 0; i--)
if (dep[u] - (1 << i) >= dep[v])
{
sum=max(sum, mx[u][i]);
u=anc[u][i];
}
if (u == v) return sum;
for (int i=17; i >= 0; i--)
if (dep[u] - (1 << i) >= 1 && anc[u][i] != anc[v][i])
{
sum=max(max(sum, mx[u][i]), mx[v][i]);
u=anc[u][i], v=anc[v][i];
}
sum=max(max(sum, mx[u][0]), mx[v][0]);
return sum;
}
void Solve()
{
while (Q--)
{
int ope; scanf("%d", &ope);
if (ope == 1)
{
int x, p; scanf("%d%d", &x, &p); p++;
add(t[x], -1);
t[x]=p; if (t[x] >= 200001) t[x]=200001;
add(t[x], 1);
}
else
{
int u, v; scanf("%d%d", &u, &v);
int val=Find(u, v);
printf("%d\n", K - getsum(val - 1));
}
}
}
int main()
{
Read();
sort(E + 1, E + 1 + M, cmp);
Init();
Build(1, 0, 1);
Solve();
return 0;
}

喵哈哈村的魔法考试 Round #21 (Div.2) 题解的更多相关文章

  1. 喵哈哈村的魔法考试 Round #2 (Div.2) 题解

    喵哈哈村的魔法考试 Round #2 (Div.2) 题解 A.喵哈哈村的战争 题解: 这道题就是for一遍,统计每个村子的战斗力的和,然后统计哪个村子的战斗力和大一点就好了. 唯一的坑点,就是这道题 ...

  2. 喵哈哈村的魔法考试 Round #1 (Div.2) 题解

    喵哈哈村的魔法考试 Round #1 (Div.2) 题解 特别感谢出题人,qscqesze. 也特别感谢测题人Xiper和CS_LYJ1997. 没有他们的付出,就不会有这场比赛. A 喵哈哈村的魔 ...

  3. 喵哈哈村的魔法考试 Round #7 (Div.2) 题解

    喵哈哈村的魔法考试 Round #7 (Div.2) 注意!后四道题来自于周日的hihocoder offer收割赛第九场. 我建了个群:欢迎加入qscoj交流群,群号码:540667432 大概作为 ...

  4. 喵哈哈村的魔法考试 Round #1 (Div.2) 题解&源码(A.水+暴力,B.dp+栈)

    A.喵哈哈村的魔法石 发布时间: 2017年2月21日 20:05   最后更新: 2017年2月21日 20:06   时间限制: 1000ms   内存限制: 128M 描述 传说喵哈哈村有三种神 ...

  5. 喵哈哈村的魔法考试 Round #19 (Div.2) 题解

    题解: 喵哈哈村的魔力源泉(1) 题解:签到题. 代码: #include<bits/stdc++.h> using namespace std; int main(){ long lon ...

  6. 喵哈哈村的魔法考试 Round #14 (Div.2) 题解

    喵哈哈村的四月半活动(一) 题解: 唯一的case,就是两边长度一样的时候,第三边只有一种情况. #include <iostream> #include <cstdio> # ...

  7. 喵哈哈村的魔法考试 Round #4 (Div.2) 题解

    有任何疑问,可以加我QQ:475517977进行讨论. A 喵哈哈村的嘟嘟熊魔法(1) 题解 这道题我们只要倒着来做就可以了,因为交换杯子是可逆的,我们倒着去模拟一遍就好了. 有个函数叫做swap(a ...

  8. 喵哈哈村的魔法考试 Round #20 (Div.2) 题解

    题解: A 喵哈哈村的跳棋比赛 题解:其实我们要理解题意就好了,画画图看看这个题意.x<y,那么就交换:x>y,那么x=x%y. 如果我们经过很多次,或者y<=0了,那么就会无限循环 ...

  9. 喵哈哈村的魔法考试 Round #18 (Div.2) 题解

    喵哈哈村的古怪石碑(一) 题解:暴力check一下是等比数列还是等差数列,然后输出答案即可.注意如果数据范围是1e9的话,就要快速幂了. 代码: #include <cstdio> #in ...

随机推荐

  1. 【转】理解*(void**)

    #include <stdio.h> int main() { int *p; ; unsigned ; p = &a; printf("%d\n", *p); ...

  2. cocos2d-x在App中的应用

    cocos2d-x是一个应用广泛的开源游戏引擎,主要是应用与开发2D游戏,开源运行于多个平台,如果只是针对于移动端平台而言,可以运行于android和ios平台. cocos2d-x目前的版本是3.1 ...

  3. oracle11g客户端配置及使用(Windows系统)

    一.安装Oracle客户端 本文环境: 操作系统:Windows XP Pro sp3(简体中文)32位 1. 首先去官网下载Oracle 11g (1) win32_11gR2_database_1 ...

  4. CSS Grid 布局

    CSS Grid 布局是 CSS 中最强大的布局系统.与 flexbox 的一维布局系统不同,CSS Grid 布局是一个二维布局系统,也就意味着它可以同时处理列和行.通过将 CSS 规则应用于 父元 ...

  5. Storm的部署

    配置方案如下 node1 Nimbus zookeeper node2 Supervisor zookeeper node3 Supervisor zookeeper node4 Supervisor ...

  6. 约数 求反素数bzoj1053 bzoj1257

    //约数 /* 求n的正约数集合:试除法 复杂度:O(sqrt(n)) 原理:扫描[1,sqrt(N)],尝试d能否整除n,若能,则N/d也能 */ ],m=; ;i*i<=n;i++){ ){ ...

  7. hdu 1542 线段树+扫描线 学习

    学习扫描线ing... 玄学的东西... 扫描线其实就是用一条假想的线去扫描一堆矩形,借以求出他们的面积或周长(这一篇是面积,下一篇是周长) 扫描线求面积的主要思想就是对一个二维的矩形的某一维上建立一 ...

  8. python访问百度地图接口并返回信息

    import urllib.parse import urllib.request data = urllib.parse.urlencode({'address': '广东省湛江市霞山区', 'ou ...

  9. 这篇文章讲得精彩-深入理解 Python 异步编程(上)!

    可惜,二和三现在还没有出来~ ~~~~~~~~~~~~~~~~~~~~~~~~~ http://python.jobbole.com/88291/ ~~~~~~~~~~~~~~~~~~~~~~~~~~ ...

  10. [转] JavaScript:彻底理解同步、异步和事件循环(Event Loop)

    一. 单线程 我们常说“JavaScript是单线程的”. 所谓单线程,是指在JS引擎中负责解释和执行JavaScript代码的线程只有一个.不妨叫它主线程. 但是实际上还存在其他的线程.例如:处理A ...