$ \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. freerradius 错误:pap: WARNING: No "known good" password found for the user

    具体错误如下: 1) # Executing section authorize from file /usr/local/etc/raddb/sites-enabled/default(1)   a ...

  2. c# 界面自适应大小

    采用在窗体事件SizeChanged里面代码控制大小和位置,达到自动适应窗体大小,这样做调整起来方便. private void FrmMain_SizeChanged(object sender, ...

  3. centos中进程管理工具

    进程管理:  二进制的格式为ELF,是CPU指令集中的指令  程序=指令+数据,  进程是程序的副本,可以有多个  内核是一个资源调度监视器  Linux是抢占式多任务  内存被事先划分成多个相同大小 ...

  4. Spring MVC注解配置

    http://blog.csdn.net/eventys/article/details/8208808

  5. Linux常用命令4(grep、df、du、awk、su、ll)

    [grep命令] grep常用用法 [root@www ~]# grep [-acinv] [--color=auto] '搜寻字符串' filename选项与参数:-a :将 binary 文件以 ...

  6. unicode-range特定字符使用font-face自定义字体

    链接: https://www.zhangxinxu.com/wordpress/2016/11/css-unicode-range-character-font-face/

  7. html中子界面与父界面相互操作或传值

    一.在使用iframe的页面,要操作这个iframe里面的DOM元素可以用: contentWindow.contentDocument(测试的时候chrom浏览器,要在服务器环境下) content ...

  8. 深入理解JS中的变量及变量作用域

    JS的变量有两种,“全局变量”和“局部变量”. “全局变量”声明在函数外部,可供所有函数使用,(全局变量属于window)而“局部变量”声明在函数体内部,只能在定义该变量的函数体内使用. 1.全局变量 ...

  9. 最全的基于MFC的ActiveX控件开发教程

    浏览器插件之ActiveX开发(一) 一般的Web应用对于浏览器插件能不使用的建议尽量不使用,因为其涉及到安全问题以及影响用户安装(或自动下载注册安装)体验问题.在有特殊需求(如涉及数据安全的金融业务 ...

  10. -webkit-line-clamp 限制多行文字

    一.应用 CSS代码: .box { width: 100px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: ve ...