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.倍数问题
#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.斐波那契
#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;
}