大二上第一周学习笔记

周三

P3586 [POI2015]LOG(猜结论 + 动态开点线段树)

首先猜一个结论

大于等于s的肯定可以选

小于s的和只要大于剩下要选的数乘以s就行了

用动态开点线段树维护

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

typedef long long ll;
const int N = 1e6 + 10;
int ls[N << 6], rs[N << 6], a[N], cnt, n, m, root;           //注意root一定一开始设为0
ll t1[N << 6], t2[N << 6];

void up(int k) 
{ 
	t1[k] = t1[ls[k]] + t1[rs[k]];
	t2[k] = t2[ls[k]] + t2[rs[k]];
}

void modify(int& k, int l, int r, int x, int p)
{
	if(!k) k = ++cnt;                                //动态开点
	if(l == r)
	{
		t2[k] += p;
		t1[k] = t2[k] * x;
		return;
	}
	int m = l + r >> 1;
	if(x <= m) modify(ls[k], l, m, x, p);
	else modify(rs[k], m + 1, r, x, p);
	up(k);
}

ll query1(int k, int l, int r, int L, int R)
{
	if(!k) return 0;                                 //询问时询问到空节点返回0
	if(L <= l && r <= R) return t1[k];
	int m = l + r >> 1; ll res = 0;
	if(L <= m) res += query1(ls[k], l, m, L, R);
	if(R > m) res += query1(rs[k], m + 1, r, L, R);
	return res;
}

int query2(int k, int l, int r, int L, int R)
{
	if(!k) return 0;
	if(L <= l && r <= R) return t2[k];
	int m = l + r >> 1, res = 0;
	if(L <= m) res += query2(ls[k], l, m, L, R);
	if(R > m) res += query2(rs[k], m + 1, r, L, R);
	return res;
}

int main()
{
	scanf("%d%d", &n, &m);
	modify(root, 0, 1e9, 0, n);  
	while(m--)
	{
		char op[5]; int x, y;
		scanf("%s%d%d", op, &x, &y);
		if(op[0] == 'U')
		{
			modify(root, 0, 1e9, a[x], -1); 
			modify(root, 0, 1e9, a[x] = y, 1);
		}
		else
		{
			int t = query2(root, 0, 1e9, y, 1e9);
			ll sum = query1(root, 0, 1e9, 0, y - 1); 
			if(sum >= 1LL * (x - t) * y) puts("TAK");
			else puts("NIE");
		}
	}

	return 0;
}

P3809 【模板】后缀排序

后缀数组模板

/*
    sa[i] 排名为i的后缀的位置
    height lcp(sa[i], sa[i - 1]) 即排名为i的后缀与排名为i−1的后缀的最长公共前缀
    H[i]:height[rak[i]],即i号后缀与它前一名的后缀的最长公共前缀

    最长公共子串(可重叠) height数组最大值 因为最长公共前缀的两个子串的排名一定相邻
    本质不同的字串数目 枚举每一个后缀i 对答案的贡献为 len − sa[i] + 1 − height[i]
    两个后缀的最大公共前缀 令x=rank[i],y=rank[j],x < y,那么lcp(i,j)=min(height[x+1],height[x+2]…height[y])。lcp(i,i)=n-sa[i]。 用ST表或线段树
*/

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int N = 1e6 + 10;
int x[N], y[N], c[N], sa[N], rk[N], height[N], wt[30];
int n, m;
char s[N];

void get_SA()
{
    _for(i, 1, n) ++c[x[i] = s[i]];
    _for(i, 2, m) c[i] += c[i - 1];
    for(int i = n; i >= 1; i--) sa[c[x[i]]--] = i;
    for(int k = 1; k <= n; k <<= 1)
    {
        int num = 0;
        _for(i, n - k + 1, n) y[++num] = i;
        _for(i, 1, n) if(sa[i] > k) y[++num] = sa[i] - k;
        _for(i, 1, m) c[i] = 0;
        _for(i, 1, n) ++c[x[i]];
        _for(i, 2, m) c[i] += c[i - 1];
        for(int i = n; i >= 1; i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
        swap(x, y);
        x[sa[1]] = 1; num = 1;
        _for(i, 2, n) 
            x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? num : ++num;
        if(num == n) break;
        m = num;
    }
}

void get_height()
{
    int k = 0;
    _for(i, 1, n) rk[sa[i]] = i;
    _for(i, 1, n)
    {
        if(rk[i] == 1) continue;
        if(k) k--;
        int j = sa[rk[i] - 1];
        while(j + k <= n && i + k <= n && s[i + k] == s[j + k]) k++;
        height[rk[i]] = k;
    }
}

int main()
{
    scanf("%s", s + 1);
    n = strlen(s + 1);
    m = 122;                //m表示字符个数 ascll('z')=122 
                            //桶的范围是1~m
    get_SA();
    _for(i, 1, n) printf("%d ", sa[i]);

    return 0;
}

周日

最近做了一些题,一直没整理,现在整理一下

之后的比赛心态一定要好,冷静而有压力

不要太紧张,太想比好

bzoj 1406(因数)

可以推出n | (x + 1)(x - 1)

令n = a * b

a | (x + 1) 且 b | (x - 1)

b | (x + 1) 且 a | (x - 1)

因此根号枚举因子,然后枚举因子的倍数即可

枚举后半段因子的倍数显然更优秀

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

set<int> ans;

int main()
{
	int n; scanf("%d", &n);
	for(int a = 1; a * a <= n; a++)
		if(n % a == 0)
		{
			int b = n / a;
			for(int i = 0; i < n - 1; i += b) if((i + 2) % a == 0) ans.insert(i + 1);
			for(int i = b; i < n + 1; i += b) if((i - 2) % a == 0) ans.insert(i - 1);
		}
	
	if(ans.empty()) puts("None");
	else for(int x: ans) printf("%d\n", x);
	
	return 0;
}

CF877D Olya and Energy Drinks(bfs + 剪枝)

这题关键是剪枝

我写的时候,剪少了就T,剪多了就WA

关键是拓展的时候

如果f[x][y] + 1> f[xx][yy]

这时候直接break了因为之后的点都可以通过f[xx][yy]去拓展,时间一样

如果f[x][y] + 1 == f[xx][yy] 这个时候不加入队列,但要继续拓展

接下来可能可以拓展到更加优秀的点

其实并不难,只是太紧张了没想到

今晚比赛一定沉下心,冷静,以平常心去对待

今晚的对手是我自己,不是别人。是对自己的心态

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)  
using namespace std;

const int N = 1e3 + 10;
int f[N][N], n, m, k;
char s[N][N];
int dir[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};
struct node
{
	int x, y, step;
};
 
int main()
{
	scanf("%d%d%d", &n, &m, &k);
	_for(i, 1, n) scanf("%s", s[i] + 1);

	int x1, y1, x2, y2;
	scanf("%d%d%d%d", &x1, &y1, &x2, &y2);

	queue<node> q;
	q.push(node{x1, y1, 0});
	memset(f, 0x3f, sizeof f);
	f[x1][y1] = 0;

	while(!q.empty())
	{
		node u = q.front(); q.pop();
		int x = u.x, y = u.y;
		if(u.step != f[x][y]) continue;

		rep(j, 0, 4)
			_for(i, 1, k)
			{
				int xx = x + dir[j][0] * i, yy = y + dir[j][1] * i;
				if(xx < 1 || xx > n || yy < 1 || yy > m || s[xx][yy] == '#' || f[xx][yy] <= f[x][y]) break;
				if(f[x][y] + 1 < f[xx][yy])
				{
					f[xx][yy] = f[x][y] + 1;
					q.push(node{xx, yy, f[xx][yy]});
				}
			}
	}

	if(f[x2][y2] > 1e9) puts("-1");
	else printf("%d\n", f[x2][y2]);
	
	return 0;
}

T199660 tree(递推)

考察了两个递推

首先可以发现,对于n,可以分成几棵相同的树

所以可以先求出一颗树有多少种构造方式

然后再求n个节点有多少构造方式

对于一颗树,可以这么理解

去掉根节点,就剩下n-1个节点,这n-1个节点可以为1颗子树,也就是dp[n - 1]

可以被2整除的话就两颗子树,就dp[(n - 1) / 2]

所以枚举因子就可以了

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)  
using namespace std;

const int mod = 998244353;
const int N = 1e5 + 10;
int dp[N], ans[N], n;
 
int main()
{
	scanf("%d", &n);
	dp[1] = dp[2] = 1;
	_for(i, 3, n)
		for(int j = 1; j * j <= i - 1; j++)
			if((i - 1) % j == 0)
			{
				dp[i] = (dp[i] + dp[j]) % mod;
				if(j * j != i - 1) dp[i] = (dp[i] + dp[(i - 1) / j]) % mod;
			}
				
	int ans = 0;
	for(int i = 1; i * i <= n; i++)
		if(n % i == 0)
		{
			ans = (ans + dp[i]) % mod;
			if(i * i != n) ans = (ans + dp[n / i]) % mod;
		}
	printf("%d\n", ans);
	
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值