C题: Destroying Array codeforces 772C
题意:给出一个长度为n的序列,每次删除一个(删除之后序列断开),求最大连续子段和。
思路:可以逆向来考虑,每次添加一个数,只有可能把他左边或右边的那两个数连接起来,所以用一个标记来判断相邻的两个数是否在之前已被添加,如果被添加,用并查集把这两个点合并,然后更新最大值。
附上代码:
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stdlib.h>
#include<set>
#include<vector>
#include<queue>
#include<map>
using namespace std;
const int maxn = 1e5+10;
const int inf = 0x3f3f3f3f;
typedef long long ll;
const int mod=1e4+7;
//#define int long long;
const double eps=1e-6;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<='0'||ch>'9'){
if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
int n;
ll fa[maxn],vis[maxn],sum[maxn],ans[maxn];
ll num[maxn];//原序列
ll creat[maxn];//删除数
int find(int x){
return fa[x]==x?fa[x]:fa[x]=find(fa[x]);
}
void baba(int x,int y){
int fx=find(x),fy=find(y);
if(fx!=fy){
fa[fx]=fy;
sum[fy]+=sum[fx];//更新连续子段和
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
fa[i]=i;
scanf("%d",&num[i]);
}
for(int i=1;i<=n;i++){
scanf("%d",&creat[i]);
}
ans[n]=0;//删完数必定为0
for(int i=n;i>=1;i--){
int temp=creat[i];
sum[temp]=num[temp];
vis[temp]=1;
if(temp!=1&&vis[temp-1]){
//判断左边是否有数
baba(temp,temp-1);
}
if(temp!=n&&vis[temp+1]){
//判断右边是否有数
baba(temp,temp+1);
}
ans[i-1]=max(ans[i],sum[find(temp)]);//取出最大值
}
for(int i=1;i<=n;i++){
printf("%lld\n",ans[i]);
}
}
D题: Chemical table codeforecs 1013D
题意:有一块n*m的区域,其中有q个格子带有标记,如下图所示,若有(r1,c1),(r1,c2),(r2,c1),那么(r2,c2)就可以自动生成标记,问若最后所有的格子都被标记,还需要的最小标记数。
思路:若原区域中没有标记,那么我们最少需要放n+m-1个标记,如下图所示。之后就是等效替换放进去的q个标记,这时就可以用到并查集。
附上代码:
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stdlib.h>
#include<set>
#include<vector>
#include<queue>
#include<map>
using namespace std;
const int maxn = 4e5+10;
const int inf = 0x3f3f3f3f;
typedef long long ll;
const int mod=1e4+7;
//#define int long long;
const double eps=1e-6;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<='0'||ch>'9'){
if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
int n,m,q;
int fa[maxn];
int ans;
int find(int x){
return fa[x]==x?fa[x]:fa[x]=find(fa[x]);
}
void baba(int x,int y){
int fx=find(x),fy=find(y);
if(fx!=fy){
//可以等效替换标记 ,ans--;
fa[fx]=fy;
ans--;
}
}
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n+m;i++){
fa[i]=i;
}
ans=n+m-1;//最多需要标记的数量
while(q--){
int x,y;
scanf("%d%d",&x,&y);
baba(x,y+n);
}
printf("%d\n",ans);
}
G题:ZJnu stadium HDU 3047
题意:给两个数n,m分别代表n个人,m对关系: 下面输入的就是这m对关系,a,b,x;表示的是a,b相距x个距离;然后判断输入的是否与这个数的之前的数信息一致, 输出不一致的总数;
题解:用一个sum数组记录他到他的祖先的距离,接下来就是用带权并查集公式推导求解,要注意方向。
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stdlib.h>
#include<set>
#include<vector>
#include<queue>
#include<map>
using namespace std;
const int maxn = 5e4+10;
const int inf = 0x3f3f3f3f;
typedef long long ll;
//const int mod=1e4+7;
//#define int long long;
const double eps=1e-6;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<='0'||ch>'9'){
if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
int fa[maxn];
int sum[maxn];//点到根节点的距离
int find(int x){
if(fa[x]!=x){
int temp=fa[x];
fa[x]=find(fa[x]);
sum[x]+=sum[temp];
}
return fa[x];
}
int main(){
int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
int ans=0;
for(int i=1;i<=n;i++){
fa[i]=i;
sum[i]=0;
}
while(m--){
int a,b,s;
scanf("%d%d%d",&a,&b,&s);
int fx=find(a),fy=find(b);
if(fx==fy){
//若之前有关系
if(sum[a]-sum[b]!=s) ans++; //判断是否与之前的关系相悖
}
else{
//若之前没有关系,则建立新的关系
fa[fx] = fy;
sum[fx] = s-sum[a]+sum[b];
}
}
printf("%d\n",ans);
}
}