疑难杂题

1.修改数组

ACWing1242. 修改数组
在这里插入图片描述
输入样例:

5
2 1 1 3 4

输出样例:

2 1 3 4 5

在这里插入图片描述

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1100010;
int p[N];
int find(int x)
{
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}
int main()
{
    int n;
    cin >> n;
    for(int i = 1; i <N; i++) p[i] = i;
    for(int i = 1; i <= n; i++)
    {
        int x;
        scanf("%d",&x);
        x = find(x);
        printf("%d ",x);
        p[x] = x+1;
    }
    return 0;
}

2.倍数问题

AcWing1234. 倍数问题
在这里插入图片描述
在这里插入图片描述

#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
const int N = 1010;
int n, k;
vector<int> a[N];
int f[4][N];
int main()
{
    cin >>n >>k;
    for(int i = 0; i <n; i++) 
    {
        int x;
        scanf("%d", &x);
        a[x%k].push_back(x);
    }
   memset(f,-0x3f,sizeof f);
   f[0][0] = 0;
   
   //x%k的范围从0~k-1
   for(int i = 0; i < k; i++)
   {
       sort(a[i].begin(),a[i].end());//排序
       reverse(a[i].begin(),a[i].end());//翻转,从大到小
       
       for(int u = 0; u < 3&&a[i].size(); u++)//枚举前三大的数
       {
           int x = a[i][u];
           for(int j = 3; j >= 1; j--)
           for(int t = 0; t <k; t++)
           f[j][t] = max(f[j][t],f[j-1][(t-x%k+k)%k]+x);
       }
   }
   cout << f[3][0]<<endl;
    return 0;
}

3.斐波那契

AcWing1213. 斐波那契
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long LL;
LL p;
LL qmul(LL a, LL b)//龟速乘法
{
    LL res = 0;
    while(b)
    {
        if(b&1) res = (res + a)%p;//如果b是奇数
        a = (a+a)%p;//乘法变加法
         b >>= 1;
    }
    return res;
}
void mul(LL c[][2],LL a[][2],LL b[][2])//c = a*b,矩阵相乘
{
    static LL t[2][2];
    memset(t,0,sizeof t);
    for(int i = 0; i < 2; i++)
     for(int j = 0; j < 2; j++)
      for(int k = 0; k < 2; k++)
       t[i][j] = (t[i][j]+qmul(a[i][k],b[k][j]))%p;
       
       memcpy(c,t,sizeof t);
}
LL F(LL n)//求第n项的值
{
    if(!n) return 0;//如果n是0,返回0
    LL f[2][2] = {1,1};
    LL a[2][2] = {{0,1},{1,1}};
    for(LL k = n-1; k ; k>>=1)
    {
        if(k&1) mul(f,f,a);//f = f*a;
        mul(a,a,a);//a = a*a;
    }
    return f[0][0];
}
LL H(LL m, LL k)//(F(m-1)*F(k)-1)%F(m)
{
    if(k%2)//k是奇数
    return F(m-k)-1;
    else
    {
        if(k==0 || m == 2 && m-k == 1) return F(m)-1;
        else return F(m)-F(m-k)-1;
    }
}

LL G(LL n, LL m)//(F(n)-1)%F(m)
{
    if(m%2 == 0)//如果m是偶数
    {
        if(n/m%2 == 0)//n/m是偶数
        {
            if(n%m == 0) return (F(m)-1);
            else return F(n%m)-1;
        }
        else// n/m是奇数
        {
            return H(m,n%m);
        }
    }
    else//m是奇数
    {
        if(n/m%2 == 0 && n/2/m%2 == 0) 
        {
            if(n%m == 0) return F(m)-1;
            else return F(n%m)-1;
        }
        else if(n/m%2 == 0 && n/2/m%2 == 1)
        {
            if(m==2 && n%m == 1) return F(m)-1;
            else return (F(m)-F(n%m)-1);
        }
        else if(n/m%2 == 1 && n/2/m%2 == 0)
        {
            return H(m,n%m);
        }
        else
        {
            if(n%m%2)
            {
                if(m == 2&& m-n%m == 1) return F(m)-1;
                else return (F(m) -F(m-n%m)-1);
            }
            else
            {
                return F(m-n%m)-1;
            }
        }
    }
}
int main()
{
    LL n, m;
  
    while(cin >> n >>m >>p) cout << (G(n+2,m)%p+p)%p <<endl;
    return 0;
    
}

4.距离

AcWing1171. 距离
在这里插入图片描述
输入样例1:

2 2 
1 2 100 
1 2 
2 1

输出样例1:

100
100

输入样例2:

3 2
1 2 10
3 1 15
1 2
3 2

输出样例2:

10
25
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef pair<int,int> PII;
const int N =20010, M = N *2;
int n,m;
int h[N],e[M],ne[M],w[M],idx;
int p[N];
int res[N];//询问结果
int st[N];
int d[N];//每个点到1号点的距离
vector<PII> query[N];//询问,first存查询的另外一个点,second存查询编号
void add(int a,int b,int c)
{
    e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}
void dfs(int u,int fa)
{
    for(int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if(j == fa) continue;
        d[j] = d[u]+w[i];
        dfs(j,u);
    }
}
int find(int x)
{
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}
void targan(int u)
{
    st[u] = 1;//当前路径点标记为1
    for(int i = h[u]; i != -1; i = ne[i])// u这条路上的根节点的左下的点用并查集合并到根节点
    {
        int j = e[i];
        if(!st[j])
        {
            targan(j);//往左下搜
            p[j] = u;//从左下回溯后把左下的点合并到根节点
        }
    }
    for(auto item:query[u])//遍历所有与u相关的查询
    {
        int y = item.first, id = item.second;
        if(st[y] == 2)//如果查询的这个点已经是左下的点(已经搜索过且回溯过,标记为2)
        {
            int anc = find(y);//最近公共祖先是y的祖宗节点
            res[id] = d[u]+d[y]-d[anc]*2;// x到y的距离 = d[x]+d[y] - 2*d[lca]
        }
    }
    st[u] =2; //点u已经搜索完且要回溯了 就把st[u]标记为2
}
int main()
{
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof h);
    for(int i = 0; i < n-1; i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c),add(b,a,c);
    }
    
     // 存下询问
    for(int i = 0;i <= m; i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        if(a != b)//特判
        {
            query[a].push_back({b,i});
            query[b].push_back({a,i});//记录查询
        }
    }
    for(int i = 1; i <= n; i++) p[i] = i;
    dfs(1,-1);
    targan(1);
    for(int i = 0; i <m; i++) printf("%d\n",res[i]);
    return 0;
}

5. 剪格子

AcWing1206. 剪格子
在这里插入图片描述
在这里插入图片描述
输入样例1:

3 3
10 1 52
20 30 1
1 2 3

输出样例1:

3

输入样例2:

4 3
1 1 1 1
1 30 80 2
1 1 1 100

输出样例2:

10

首先对题意进行理解:
两个连通的部分,即分别在所分割的两部分中,任何一个点都能合法到达该部分的所有位置。
如下图所示,红色部分点与点之间都至少存在一条位于红色内部的路径,黄色部分同理。表明其为两个连通的部分。
在这里插入图片描述
如下图所示,展示的是两种不合法情况
在这里插入图片描述
使得这两个区域的数字和相等,这是本题搜索的依据,两个区域数字和相等,且只能分割为两个连通的部分,易得出如果存在这样的两个部分,那么该图中元素总和必为偶数,且每个部分中元素和是元素总和的一半,可得元素总和为奇数时,一定无法分割成满足条件的情况。
如下图所示,元素总和为60,每个部分中元素和均为30==60/2
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
解析来源于KaMtuo

#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_set>
#include<vector>
#define x first
#define y second
using namespace std;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
const int N = 10,INF = 1e8,q = 131;
int n,m;
int w[N][N];
bool st[N][N];
int p[N*N];
unordered_set<ULL> hash_table;
int sum, ans = INF;
PII cands[N*N];

int dx[4]= {-1,0,1,0}, dy[4] = {0,1,0,-1};
int find(int x)
{
    if(p[x]!= x) p[x] = find(p[x]);
    return p[x];
}
bool check_connect(int k)//检查剩余部分是否是一个连通块
{
    for(int i = 0; i <n*m; i++)//初始化所有点
    p[i] = i;//并查集
    
    int cnt = n*m-k; //剩余连通块的个数
    for(int i = 0; i < n; i++)
     for(int j = 0; j <m; j++)
      if(!st[i][j])//当前点未被搜过
      {
          for(int u = 0; u < 4; u++)
          {
              int a = i + dx[u],b = j +dy[u];
              if(a < 0 || a>= n|| b <0 || b >= m) continue;
              if(st[a][b]) continue;
              
              int p1 = find(i*m +j),p2 = find(a*m+b);
              if(p1 != p2)//两个点不在一个集合里
              {
                  p[p1] = p2;//合并集合
                  cnt--;
              }
          }
      }
      
      if(cnt != 1) return false;
      return true;
}
bool check_exist(int k)
{
    static PII bk[N*N];
    for(int i = 0; i < k; i++) bk[i] = cands[i];
    sort(bk,bk+k);
    ULL x = 0;
    for(int i = 0; i <k; i++)
    {
        x = x*q + bk[i].x + 1;
        x = x*q + bk[i].y + 1;
    }
    if(hash_table.count(x)) return true;//在哈希表中
    hash_table.insert(x);
    
    return false;
}
void dfs(int s,int k)//s表示总和,k表示当前选出的连通块有多少个点
{
    if(s == sum/2)
    {
        if(check_connect(k)) ans = min(ans,k);//检测连通性
        return;
    }
    
    vector<PII> points;//所有可以扩展到的点
    for(int i = 0; i < k;i++)//循环当前所有的备选点
    {
        int x = cands[i].x, y = cands[i].y;
        for(int j = 0; j < 4; j++)//枚举四个方向
        {
            int a = x + dx[j], b = y+dy[j];
            if(a <0 ||a >= n || b < 0 || b >= m) continue;
            if(st[a][b]) continue;
            
            cands[k] = {a,b};
            if(k + 1 <ans && !check_exist(k+1)) points.push_back({a,b});
        }
    }
    sort(points.begin(), points.end());//排序
    reverse(points.begin(), points.end());//翻转
    for(int i = 0; i <points.size(); i++)
    if(!i || points[i] != points[i-1])//去重
    {
        cands[k] = points[i];
        int x = points[i].x, y = points[i].y;
        st[x][y] = true;
        dfs(s +w[x][y],k+1);//下一层
        st[x][y] = false;//恢复现场
    }
}
int main()
{
    cin >> m >> n;
    for(int i = 0; i < n; i++)
     for(int j = 0; j <m; j++)
     {
         cin >> w[i][j];
         sum += w[i][j];
     }
     if(sum%2 == 0)//本题要求两边的和一样,所以只有偶数可行
     {
         st[0][0] = true;
         cands[0]  = {0,0};
         dfs(w[0][0],1);
     }
     if(ans == INF) ans = 0;//无解
     cout << ans <<endl;
     return 0;
}

6.组合数问题

AcWing523. 组合数问题
在这里插入图片描述
输入样例:

1 2
3 3

输出样例:

1

在这里插入图片描述

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 2010;
int c[N][N];//组合数/k的余数
int s[N][N];//前缀和
int main()
{
    int T,k;
    cin >> T>>k;
    for(int i = 0; i <N; i++)
     for(int j = 0; j <= i; j++)
      if(!j) c[i][j] = 1%k;//如果j是0,c[i][j] = 1;
      else c[i][j] = (c[i-1][j]+c[i-1][j-1])%k;
      
      for(int i = 0; i < N; i++)
       for(int j = 0; j < N; j++)
       {
           if(j <= i &&c[i][j] == 0) s[i][j] = 1;//第一个值
           if(i - 1 >= 0) s[i][j] += s[i-1][j];
           if(j - 1 >= 0) s[i][j] += s[i][j-1];
           if(i - 1 >= 0 && j-1 >= 0) s[i][j] -= s[i-1][j-1];
       }
       while(T--)
       {
           int n,m;
           cin >> n >> m;
           cout << s[n][m] << endl;
       }
       return 0;
      
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值