【HNOI模拟By lyp】Day1

1 xlk
1.1 题目描述
给定一棵大小为 n 的无根树,求满足以下条件的四元组 (a, b, c, d) 的个数:
1. 1 a < b n
2. 1 c < d n
3. 不存在一个点,使得这个点同时在点 a 到点 b 的最短路和点 c 到点 d 的最短路上。
1.2 输入格式
第一行一个数 n
接下来 n 1 行,每行两个数 s, t ,表示一条连接 a b 的边。
1.3 输出格式
输出满足条件的四元组的个数。
1.4 样例输入
4
1 2
2 3
3 4
1.5 样例输出
2
1.6 数据范围
对于 30% 的数据,满足 n 50 ;
对于 100% 的数据,满足 1 n 80000 。

xlk
容斥。

对于每棵子树,求出一条链在子树外,一条链过子树根的方案数。这样会算重。于是再枚举每棵子树,减去一条链过根,一条链在某棵子树内的方案数。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define LY(p) freopen (p".in", "r", stdin); freopen (p".out", "w", stdout)
#define LL long long
#define dbl double
#define ld long double
#ifdef WIN32
#define LLD "%I64d"
#else
#define LLD "%lld"
#endif
#define N 80010
int n, x, y;
int h[N], ent;
int siz[N];
LL f[N], g[N], ans;

struct edge {
int v, n;

edge (int y = 0, int t = 0): v(y), n(t) {}
} e[N << 1];

void link (int x, int y) {e[++ ent] = edge (y, h[x]), h[x] = ent;}

void dfs (int o, int ft) {
siz[o] = 1;
LL d = 0;
for (int x = h[o], y; y = e[x].v, x; x = e[x].n)
if (y != ft) {
dfs (y, o);
ans += g[o] * g[y] + f[o] * siz[y] + siz[o] * f[y];
f[o] += d * siz[y] + siz[o] * g[y] + f[y];
g[o] += siz[o] * siz[y] + g[y];
siz[o] += siz[y];
d += g[y];
}
}

int main()
{
LY("xlk");
scanf ("%d", &n);
for (int i = 1; i < n; i++)
scanf ("%d %d", &x, &y), link (x, y), link (y, x);

dfs (1, 0);
printf (LLD, ans << 1);
return 0;
}

2 wwwwodddd
2.1 题目描述
定义一个数 x 是 Happy Number ,当且仅当求该数字所有数位的平方和,得到的新数再次求所
有数位的平方和,如此重复进行,最终结果为  。
例如 19 就是个 Happy Number :
19 12 + 92 = 82
82 82 + 22 = 68
68 62 + 82 = 100
100 12 + 02 + 02 = 1
给定 L, R ,求 [L, R] 内 Happy Number 的个数。
2.2 输入格式
第一行一个正整数 T ,表示数据组数。
接下来 T 行,每行两个正整数,表示 L, R
2.3 输出格式
对于每组测试数据,输出一个整数表示这个区间内 Happy Number 的个数。
2.4 样例输入
2
2 6
1 10
2.5 样例输出
0 3
2.6 数据范围
对于 30% 的数据,满足 R 105
对于 100% 的数据,满足 1 L R 1018, 1 T 200 。

wwwwodddd
数位 dp 。

显然任意数经过一次变换后一定会小于 2000 ,于是预处理出 f[i][j] 表示 i 位数,平方和为 j 的个数,再预处理出 [0, 2000] 内所有的 Happy Number ,数位 dp 即可。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define LY(p) freopen (p".in", "r", stdin); freopen (p".out", "w", stdout)
#define LL long long
#define dbl double
#define ld long double
#ifdef WIN32
#define LLD "%I64d"
#else
#define LLD "%lld"
#endif
bool vis[1500], hap[1500];
int T, m, now = 0;
LL L, R, f[2][1500][2];

int C (int o) {
int s = 0;
while (o)
s += (o % 10) * (o % 10), o /= 10;
return s;
}

int dfs (int o) {
if (vis[o]) return hap[o];
vis[o] = 1;
return hap[o] = (o > 1? dfs (C (o)) : 1);
}

void prep() {
m = 1458;
for (int i = 1; i <= m; i++)
dfs (i);
}

LL work (LL s) {
memset (f, 0, sizeof (f));
f[now][0][0] = 1;
for (int nt = 0, ns = 0; s; s /= 10) {
nt = s % 10;
for (int s = 0; s <= ns; s++)
for (int r = 0; r <= 1; r++)
if (f[now][s][r])
for (int i = 0; i <= 9; i++)
f[now ^ 1][s + i * i][i > nt || (r && i == nt)] += f[now][s][r];

memset (f[now], 0, sizeof (f[now])), now ^= 1;
ns += 81;
}
LL ans = 0;
for (int s = 1; s <= m; s++)
if (hap[s])
ans += f[now][s][0];
return ans;
}

int main()
{
LY("wwwwodddd");
prep();
scanf ("%d", &T);
for (int TT = 1; TT <= T; TT++) {
scanf (LLD LLD, &L, &R);
printf (LLD"\n", work (R) - work (L - 1));
}
return 0;
}

3 zradiance
3.1 题目描述
给定一个序列 S ,支持以下操作:
• 插入一个数 x ,使得插入后 x 是序列的第 k 个元素
• 删除第 k 个元素
• 翻转区间 [L, R]
• 给定区间 [L, R] ,求

∑  (S− Sx)

     LxyR
3.2 输入格式
第一行两个整数 n, m ,表示初始时序列长度以及操作数。
第二行 n 个整数,描述初始序列。
接下来 m 行,每行格式为:
• 1 k x
• 2 k
• 3 L R
• 4 L R
意义见题面。
3.3 输出格式
对于每次询问,输出要求表达式的值。
3.4 样例输入
2 5
1 2
4 1 2
1 1 3
2 2
3 1 2
4 1 2
3.5 样例输出
1 1
3.6 数据范围
对于 30% 的数据,满足 n, m 103
对于 100% 的数据,满足 n, m 105

zradiance
显然区间具有可加性。于是就是一个普通的序列维护题。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define LY(p) freopen (p".in", "r", stdin); freopen (p".out", "w", stdout)
#define LL long long
#define dbl double
#define ld long double
#ifdef WIN32
#define LLD "%I64d"
#else
#define LLD "%lld"
#endif
#define N 100010
int n, m, opt, x, k, L, R;
int val[N];

struct SplayTree {
struct node {
int ch[2], fa, siz;
int rev;
LL val, lsum, rsum, sum;
};

int siz, rt;
node a[N << 1];

SplayTree(): siz(0), rt(0) {}

int newnode (int v, int ft) {
return siz ++, a[siz].val = a[siz].sum = a[siz].lsum = a[siz].rsum = v, a[siz].rev = 0, a[siz].siz = 1, a[siz].fa = ft, siz;
}

void update (int o) {
a[o].siz = a[ a[o].ch[0] ].siz + a[ a[o].ch[1] ].siz + 1;
a[o].sum = a[ a[o].ch[0] ].sum + a[ a[o].ch[1] ].sum + a[o].val;
a[o].lsum = a[ a[o].ch[0] ].lsum + a[ a[o].ch[1] ].lsum + (a[o].val + a[ a[o].ch[1] ].sum) * (a[ a[o].ch[0] ].siz + 1);
a[o].rsum = a[ a[o].ch[1] ].rsum + a[ a[o].ch[0] ].rsum + (a[o].val + a[ a[o].ch[0] ].sum) * (a[ a[o].ch[1] ].siz + 1);
}

void rev (int o) {
a[o].rev ^= 1;
swap (a[o].lsum, a[o].rsum);
swap (a[o].ch[0], a[o].ch[1]);
}

void push (int o) {
if (a[o].rev)
rev (a[o].ch[0]), rev (a[o].ch[1]), a[o].rev = 0;
}

void rotate (int o, int d) {
int f = a[o].fa, ff = a[f].fa, fd = (a[ff].ch[1] == f);
a[f].ch[d ^ 1] = a[o].ch[d], a[ a[o].ch[d] ].fa = f;
a[o].ch[d] = f, a[f].fa = o;
a[ff].ch[fd] = o, a[o].fa = ff;
update (f);
if (rt == f) rt = o;
}

void splay (int o, int ft = 0) { // note that the way to find the node
for (int f, ff, d, fd; a[o].fa != ft;) {
f = a[o].fa, ff = a[f].fa;
d = a[f].ch[1] == o, fd = a[ff].ch[1] == f;
if (ff == ft)
rotate (o, d ^ 1);
else
if (d == fd)
rotate (f, fd ^ 1), rotate (o, d ^ 1);
else
rotate (o, d ^ 1), rotate (o, fd ^ 1);
}
update (o); // remember it
}

int find (int k) {
int o = rt;
while (1) {
push (o); // push the tag while searching the node
if (k == a[ a[o].ch[0] ].siz + 1)
return o;
if (k < a[ a[o].ch[0] ].siz + 1)
o = a[o].ch[0];
else
k -= a[ a[o].ch[0] ].siz + 1, o = a[o].ch[1];
}
}

int get (int L, int R) { // original rank
int l, r;
r = find (R + 1 + 1), splay (r); // all rank + 1
l = find (L - 1 + 1), splay (l, r); // all rank + 1
return l;
}

void insert (int k, int v) {
int o = get (k, k - 1);
a[o].ch[1] = newnode (v, o);
splay (o);
}

void remove (int k) {
int o = get (k, k);
a[o].ch[1] = 0;
splay (o);
}

void reverse (int L, int R) {
int o = a[get (L, R)].ch[1];
rev (o), push (o);
splay (o);
}

LL query (int L, int R) {
int o = a[get (L, R)].ch[1];
LL s = a[o].lsum - a[o].rsum;
push (o), splay (o);
return s;
}

void build (int &o, int ft, int *val, int l, int r) {
if (l > r) return;
int mid (l + r >> 1);
o = newnode (val[mid], ft);
build (a[o].ch[0], o, val, l, mid - 1);
build (a[o].ch[1], o, val, mid + 1, r);
update (o);
}

void init (int *val, int n) {
rt = newnode (0, 0), a[rt].ch[1] = newnode (0, rt);
build (a[ a[rt].ch[1] ].ch[0], a[rt].ch[1], val, 1, n);
splay (a[rt].ch[1]);
}
} *T = new SplayTree;

int main()
{
LY("zradiance");
scanf ("%d %d", &n, &m);
for (int i = 1; i <= n; i++)
scanf ("%d", val + i);
T-> init (val, n);
for (int i = 1; i <= m; i++) {
scanf ("%d", &opt);
if (opt == 1)
scanf ("%d %d", &k, &x), T-> insert (k, x);
if (opt == 2)
scanf ("%d", &k), T-> remove (k);
if (opt == 3)
scanf ("%d %d", &L, &R), T-> reverse (L, R);
if (opt == 4)
scanf ("%d %d", &L, &R), printf (LLD"\n", T-> query (L, R));
}
return 0;
}

以上题解fromZZD

时间: 2024-08-24 08:33:51

【HNOI模拟By lyp】Day1的相关文章

【HNOI模拟By lyp】Day2

1 toad1.1 题目描述有 n 个石子, A B 两人博弈, A 先手. A 首先取若干个石子(至少一个,不能取完),然后 B 和A 再轮流取石子,每次取的石子不能超过 axb ( x 表示上次取的石子数, a, b 是两个正整数参数),且至少取一个,无法操作的人输.求 n 满足什么条件时先手必胜.1.2 输入格式第一行三个正整数 T, a, b , T 表示数据组数, a, b 意义见题.接下来 T 行,每行一个正整数 n .1.3 输出格式对于每个 n ,输出 A 或 B ,表示此时 A

XJOI-NOIP2015提高组模拟题1 day1

其实这只是一道题的题解= =: 博主太弱不会T1T3: 然而我还是要吐槽一下,T2难道你们就没有一点写数据结构的心情吗! T1: 留坑(不太可能填): T2: 题意: 给出大小为n的一个四维点集,和m次询问: 每次询问给出一个点,求四维坐标均小于等于这个点的集合大小: n,m<=30000: 题解: 看到这题的第一反应是排序乱搞,noip难度应该随便玩玩就过了嘛(笑): 但是仔细看看不是这么回事! bzoj有一道题叫陌上花开--然而那个是三维的: 回忆一下,PoPoQQQ让我们搞排序+CDQ分治

【HNOI模拟By YMD】move

Description 设P(n)为从(0,0)移动到点(n,0)的不同路径数目,移动的方式有以下三种:(x,y)->(x+1,y-1),(x,y)->(x+1,y),(x+y)->(x+1,y+1),并且路径不能和第四象限有交集.求P(n),并对10^9+7取模. Input 第一行一个整数T,表示数据组数. 对于每组数据,一行一个整数n. Output 对于每组数据,输出答案. Data Range 20%:n≤10:50%:n≤10000:100%:n≤106,T≤10. Solu

省常中模拟 Test3 Day1

tile 贪心 题意:给出一个矩形,用不同字母代表的正方形填充,要求相邻的方块字母不能相同,求字典序(将所有行拼接起来)最小的方案. 初步解法:一开始没怎么想,以为策略是每次填充一个尽量大的正方形.但是很快就能找到反例.比如当一个 4*2 的矩形左半部分填充了一个 2*2 的 A 后,不应该在右半部分填充 2*2 的 B,而是应该先填一个 1*1 的 B,然后继续用 A 填充,如图. 上面这个例子中,第二种方案虽然用到了 C,但是如果按照题意将每一行拼接起来之后第二种方案的字典序显然更小. 正解

省常中模拟 Test1 Day1

临洮巨人 排序 题意:在字符串中找出 A.B.C 三个字母出现次数相同的区间个数. 初步的解法是前缀和,用 a(i), b(i), c(i) 表示在位置 i 之前(包括 i)各有 字母 A.B.C 多少个,枚举区间的左右端点 l 和r,若a(r)-a(l-1) = b(r)-b(l-1) = c(r)-c(l-1),则是一组解.O(n2) 的复杂度可以过 70%. 正解:将上式变形可得, a(r)-b(r)=a(l-1)-b(l-1) b(r)-c(r)=b(l-1)-c(l-1) 所以我们可以

东方14模拟赛之noip2015/day1/3/神奇的幻方

总时间限制:  10000ms 单个测试点时间限制:  1000ms 内存限制:  128000kB 描述 幻方是一种很神奇的N*N 矩阵:它由数字 1,2,3, … …,N*N 构成,且每行.每列及两条对角线上的数字之和都相同. 当N为奇数时,我们可以通过以下方法构建一个幻方: 首先将 1 写在第一行的中间. 之后,按如下方式从小到大依次填写每个数K(K= 2,3, …,N*N ): 若 (K−1) 在第一行但不在最后一列,则将K填在最后一行,(K−1) 所在列的右一列: 若 (K−1) 在最

省常中模拟 day1

第一题: 题目大意: 给出N个数的数列,如果相邻的两个数加起来是偶数,那么就可以把这两个数消掉,求最多能消掉多少数. 解题过程: 1.先自己手工模拟了几组数据,发现不管消除的顺序如何,最终剩下的是一定的.所以就可以每次任意找两个奇偶性相同的消掉.于是就想到可以用双向链表来模拟. 2.更好的方法:直接用一个栈来模拟,每次加入一个元素,如何和栈顶元素奇偶性一样就弹出栈顶元素. 初始得分100. 第二题: 题目大意: 给出N个数的数列,定义区间[L,R]的权值为AL and AL+1 and AL+2

[铁一中OI模拟赛]2017.8.19 Day1

T1 小Z的情书 题目链接 思考: 题目主要难度在于旋转后的位置如何,在手写了样例之后不难发现规律. #include <cstdio> #include <cstring> #define up(a,b,c) for(register int c=a;c<=b;++c) #define down(a,b,c) for(register int c=a;c>=b;--c) const int Maxn=1005; int n; bool Map[Maxn][Maxn],

Day1:T1 模拟 T2 拓扑排序

T1:模拟 自己第一天的简直跟白痴一样啊...模拟都会打错.. 当时貌似在更新最大值的时候打逗比了... if((sum[x]==max && x<maxh) || sum[x]>max){  max=sum[x];  maxh=x; //现在(也就是9月+)再看,脑袋里只有sortsortsort,连最基本的更新最大指都忘了....智商唉.... 附上代码: #include<cstdio> #include<cstring> using namesp