2024/1/19 算法笔记

题目1:最大公约数的延伸问题

P1414 又是毕业季II - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目上提及了最大公约数,但是解答却没有直接使用最大公约数doge

题目意思是 给定n个数,再给定一个k,往这n个数中取k个,求这k个数的最大公约数是多少?

然后题目的要求是k的取值为1到n全部取一遍,分别求出对应的最大公约数。

我们知道,当取的数的个数越多,最大公约数肯定是小于等于取的个数少时的答案的。因为限制会变多。

一开始很容易想到枚举n个数取k个的所有组合,然后分别用辗转相除法求最大公约数,但是复杂度明显不符合要求,于是必须换一种思路。

我们想到,k个数的公约数含义就是这k个数均含有某个因数,如果我们把所有数的因数全部求出来,发现有k个数均含有某个因数,那么这个数必然是这k个数的公约数。其中找出最大的就是它们的最大公约数。但是要如何高效的做到这点呢?考虑到对于k=1,2……,n都要求出,我们可以这么做:

* 1、 求出每个因数出现的次数。

* 2、 对于每个次数记录最大的因数。

我们在输出答案的时候 选择默契值的最大值t开始往小查找。只要这个默契值的出现次数大于等于当前我们遍历到的人数,就可以认定为它是最大公约数,因为是从大到小遍历的。

于是我们的 做法如下:

void solve(){
    int t = 0;
    cin>>n;
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        t = max(t,x);
        int m = sqrt(x);
        for(int j=1;j<=m;j++){
            if(x%j==0){
                c[j]++;  //c[]用于记录某个因子的出现次数。
                if(x!=j*j) c[x/j]++;
            }
        }
    }
    //输出结果。
    int x= t;
    for(int i=1;i<=n;i++){
        while(c[x]<i)x--;
        cout<<x<<endl;
    }
}   

题目2:前缀和&差分

基础:

前缀和解答区间查询问题:P8218 【深进1.例1】求区间和 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

解答:

int a[100005],b[100005];
void solve(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        b[i] = b[i-1]+a[i];   //这里做出前缀和
    }
    int m;
    cin>>m;
    for(int i=1;i<=m;i++){
        int l,r;
        cin>>l>>r;
        cout<<b[r]-b[l-1]<<endl;  //这里记住如何求区间和
    }
}   

前缀和+差分解答多次区间增值问题:P2367 语文成绩 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

解答:

int a[5000005],b[5000005];
void solve(){
    int n,t;
    cin>>n>>t;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        b[i] = a[i]-a[i-1];   //差分数组,事实上是后一项减去前一项的差值。
    }
    for(int i=1;i<=t;i++){
        int l,r,x;
        cin>>l>>r>>x;
        b[l]+=x;
        b[r+1]-=x;            //公式化的区间增值。记住就好,可以画图理解。
    }
    int minn=inf;
    for(int i=1;i<=n;i++){
        a[i] =a[i-1] + b[i];    //这里是差分数组转换为正常数组,也就是前缀和的逆形式。  可以观察和前面差分处理的区别。
        minn = min(a[i],minn);
    }
    cout<<minn<<endl;
}   

拓展:二维前缀和 和 二维差分

P3397 地毯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

int main()
{
	cin>>n>>t;
	while(t--)   //二维差分
	{
		cin>>lx>>ly>>rx>>ry;
		++b[lx][ly];
		--b[rx+1][ly];
		--b[lx][ry+1];
		++b[rx+1][ry+1];
	}
	for(int i=1;i<=n;i++) 
		for(int j=1;j<=n;j++) 
		{
			a[i][j]=b[i][j]+a[i-1][j]+a[i][j-1]-a[i-1][j-1];  //求原数组
			cout<<a[i][j]<<" ";
			if(j==n) cout<<endl;
		}
	return 0;
}

题目3:离散化

暂时没找到专门讲这个的题,后面再补充吧

题目4:双指针

事一个双指针的题,归类到了离散化名下。但是我还是不知道是否是离散化。

[P3029 USACO11NOV] Cow Lineup S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

做法是将距离(struct node中的x)由小到大排序,然后在一个窗口中框住含有每一个不同种类的奶牛,维护距离的最小值,然后处理窗口左边界。

struct node {
    int x,p;
}s[70005];


bool cmp(node a,node b){
    return a.x<b.x;
}

void solve(){
    int n;
    cin>>n;
    map<int,int>t;
    map<int,bool>vis;
    int sum=0;
    for(int i=1;i<=n;i++){
        cin>>s[i].x>>s[i].p;
        if(vis[s[i].p]==0){
            sum++;
            vis[s[i].p] = 1;
        }
    }    
    sort(s+1,s+1+n,cmp);
    int tail = 1;
    t[s[1].p]++;//最左边,第一格
    int tmp = 1;
    int ans = inf;
    //滑动窗口思想
    for(int i=1;i<=n;i++){
        while(tmp<sum&&tail<n){
            tail++;
            t[s[tail].p]++;
            if(t[s[tail].p]==1) tmp++;
        }
        if(tmp == sum) ans = min(ans,s[tail].x - s[i].x);
        t[s[i].p]--;
        if(t[s[i].p] == 0)tmp--;//处理左端   相当于左边界往后挪了一格。
    }
    cout<<ans<<endl;
}   

另外,今日有一场萌新赛,打得很垃圾,5/8。希望这几天能补满。

更新的时候我居然还没补,明天补上。23

posted @ 2024-01-19 22:42  Akimizuss101  阅读(32)  评论(0编辑  收藏  举报