团体赛训练 NEERC2014 Southern Sub

这套题可做题挺多的,而且有几个构造和模拟题  一些不可做题和无聊的题就没补了 

A - Nasta Rabbara

因为我是dser 赛后唯一补的题  当时比赛的时候没读懂,看没什么人过也放弃了 确实有点麻烦的题 但是很有意思

题意:给出n个点,m条边,q个询问 每个询问给一个区间l,r 问区间l,r内的边是否能构成二分图

 一般像这种动态加边删边的,就想到lct了  不过并没想到什么处理区间问题的好方法  

看了题解才知道:求出所有的最小的可以构成奇环的区间 看询问的区间是否包含至少一个这样的区间即可

如何去求呢

不停的往集合里面加边:

如果当前未联通 那么直接加入该边

如果加入当前边 构成了偶数环  那么把编号最小的边去掉

如果加入当前边 构成了奇数环 那么我们就得到了一个最小的可构成奇数环的区间  我们放入答案集合 并且删去 该区间左端点之前的所有边(包括左端点) 这样就可以继续去找下一个最小的可构成奇数环的区间

因为偶数环删除可以保证我们找到的构成奇数环的区间是最小的

#include<bits/stdc++.h>
#define lc c[x][0]
#define rc c[x][1]
#define R register int
#define I inline void 
#define pa pair<int,int>
using namespace std;
const int N = 4e5+100;
pa p[N];
int n,m,q,tot;
int f[N],c[N][2],val[N],siz[N],mi[N],r[N],st[N],vis[N];
struct edge{
	int u,v;
}e[N];
inline int in(){
	R w=0,x=0;char c=0;
	while(c<'0'||c>'9') w|=c=='-',c=getchar();
	while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return w?-x:x;
}
inline bool nroot(R x){
	return c[f[x]][0]==x||c[f[x]][1]==x;
}
I pushr(R x){
	swap(lc,rc);r[x]^=1;
}
I pushup(R x){
	siz[x]=siz[lc]+siz[rc]+(x>n);
	mi[x]=x;
	if(lc&&val[mi[lc]]<val[mi[x]]) mi[x]=mi[lc];
	if(rc&&val[mi[rc]]<val[mi[x]]) mi[x]=mi[rc];
}
I pushdown(R x){
	if(r[x]){
		if(lc) pushr(lc);
		if(rc) pushr(rc);
		r[x]=0;
	}
}
I rotate(R x){
	R y=f[x],z=f[y],k=c[y][1]==x,w=c[x][k^1];
	if(nroot(y)) c[z][c[z][1]==y]=x; c[x][k^1]=y;c[y][k]=w;
	if(w) f[w]=y; f[x]=z;f[y]=x;
	pushup(y);
}
I splay(R x){
	R y=x,z=0;
	st[++z]=y;
	while(nroot(y)) st[++z]=y=f[y];
	while(z) pushdown(st[z--]);
	while(nroot(x)){
		y=f[x],z=f[y];
		if(nroot(y)) 
			rotate((c[y][1]==x)^(c[z][1]==y)?x:y);
		rotate(x);
	}
	pushup(x);
}
I access(R x){
	for(R y=0;x;x=f[y=x])
	splay(x),rc=y,pushup(x);
}
I makeroot(R x){
	access(x);splay(x);
	pushr(x);
}
I split(R x,R y){
	makeroot(x);
	access(y);splay(y);
}
inline int findroot(R x){
	access(x);splay(x);
	while(lc) pushdown(x),x=lc;
	splay(x);
	return x;
}
inline void link(int x,int y){
    makeroot(x);
    f[x]=y;
}
inline void cut(int x,int y){
    split(x,y);
    f[x]=c[y][0]=0;
    pushup(y);//少了个儿子,也要上传一下
}
inline bool judge(R x,R y){
	makeroot(x);
	if(findroot(y)==x) return true;
	return false;
}
I clear(int st,int ed){
	for(int i = st; i <= ed; i++)
		if(vis[i]) 
		cut(i+n,e[i].u),cut(i+n,e[i].v),vis[i]=0;
}
int main(){
	n=in(),m=in(),q=in();
	for(int i = 0; i <= n; i++) val[i]=2e9,mi[i]=i;
	for(int i = 1; i <= m; i++){
		mi[i+n]=i+n;
		val[i+n]=i+n;
		e[i].u=in(),e[i].v=in();
	}
	int st=1;
	for(int i = 1; i <= m; i++){
		R u=e[i].u,v=e[i].v;
		if(judge(u,v)){
			split(u,v);
			int c = mi[v];
			if(siz[v]&1){
				cut(c,e[c-n].u);cut(c,e[c-n].v);
				vis[c-n]=0;
			}else{
				++tot;
				p[tot].first=c-n;p[tot].second=i;
				clear(st,c-n);
				st=c-n+1;
			}
			link(u,i+n);link(v,i+n);
			vis[i]=1;
		}else{
			link(u,i+n);link(v,i+n);
			vis[i]=1;
		}
	}
	for(int i = 1; i <= q; i++){
		R l,r;
		l=in(),r=in();
		int pos = lower_bound(p+1,p+1+tot,make_pair(l,0))-p;
		if(pos==tot+1||r<p[pos].second) puts("Possible");
		else puts("Impossible");
	}
	return 0;
}

B - Colored Blankets

C - Component Tree

D - Data Center

题意:首先内存大于等于m 并且数量最少 其次1最多

思路:队友给的 因为数量要最少 我们可以先确定这个数量 肯定是把内存降序排 然后选前几个总和能超出m内存的构成答案集合 确定了数量以后 把剩下的分成01两部分 每次从答案集合中 选出内存最少的并且标号是0的  交换后保证内存大于等于m 向答案里面加入内存最大的并且标号为1的  如果不行则结束

#include<stdio.h>
#include<algorithm>
using namespace std;
#define ll long long
struct node{
	ll a,b,w;
};
node x[200010];
node y[200010],z[200010];
bool cmp(node i,node j){
	return i.a>j.a;
}
int ans[200010];
int main(){
	ll n,m;
	scanf("%lld %lld",&n,&m);
	int cnty=0,cntz=0;
	for(int i=1;i<=n;i++){
		ll tempa,tempb;
		scanf("%lld %lld",&tempa,&tempb);
		node temp;
		temp.a=tempa;
		temp.b=tempb;
		temp.w=i;
		x[i]=temp;
		if(tempb==0){
			y[++cnty]=temp;
		}
		else{
			z[++cntz]=temp;
		}
	}
	sort(x+1,x+n+1,cmp);
	sort(y+1,y+cnty+1,cmp);
	sort(z+1,z+cntz+1,cmp);
	int num=0;
	ll temp=0;
	for(int i=1;i<=n;i++){
		num++;
		temp=temp+x[i].a;
		if(temp>=m){
			break;
		}
	}
	ll now=0;
	int wz,wy;
	if(cntz<=num){
		for(int i=1;i<=cntz;i++){
			now=now+z[i].a;
		}
		for(int i=1;i<=num-cntz;i++){
			now=now+y[i].a;
		}
		wz=cntz;
		wy=num-cntz;
	}
	else{
		for(int i=1;i<=num;i++){
			now=now+z[i].a;
		}
		wz=num;
		wy=0;
	}
	while(now<m){
		wz--;
		wy++;
		now=now-z[wz+1].a+y[wy].a;
	}
	printf("%d %d\n",num,wz);
	for(int i=1;i<=wz;i++){
		printf("%d",z[i].w);
		if(i==wz&&wz==num){
			printf("\n");
		}
		else{
			printf(" ");
		}
	}
	for(int i=1;i<=wy;i++){
		printf("%d",y[i].w);
		if(i==wy){
			printf("\n");
		}
		else{
			printf(" ");
		}
	}
}

  

E - Election of a Mayor

 题意好像是让第一个人的得票数大于等于一半 并且可以合并两个站的票 (合并后不能再合并) 问要合并哪些站

队友想的 我全程划水

#include<stdio.h>
int m[200010],r[200010];
int ansl[200010],ansr[200010];
int main(){
	int n;
	scanf("%d",&n);
	int s=0,h=0;
	for(int i=1;i<=n;i++){
		scanf("%d %d",&m[i],&r[i]);
		if(m[i]>r[i])s++;
	}
	for(int i=1;i<n;i++){
		if(2*s>n-h){
			break;
		}
		if(m[i]<=r[i]&&m[i+1]<=r[i+1]){
			h++;
			ansl[h]=i;
			ansr[h]=i+1;
			i++;
		}
		else if(m[i]+m[i+1]>r[i]+r[i+1]){
			if(m[i]<=r[i]&&m[i+1]>=r[i+1]){
				h++;
				ansl[h]=i;
				ansr[h]=i+1;
				i++;
			}
			else if(m[i]>=r[i]&&m[i+1]<=r[i+1]){
				h++;
				ansl[h]=i;
				ansr[h]=i+1;
				i++;
			}
		}
	}
	if(2*s>n-h){
		printf("%d\n",h);
		for(int i=1;i<=h;i++){
			printf("%d %d\n",ansl[i],ansr[i]);
		}
	}
	else{
		printf("-1");
	}
}

F - Ilya Muromets

 题意:选两段长度为k的子串 让它们的和最大 (选完一段以后 另一段可以相前并)

其实就是选两段长度为k的连续区间  让它们的和最大 能不能并无所谓  反正也可以拆成两端长度为k的连续子串

那么我们通过前缀和已经前缀最大值 选两端 让它们的和最大就行

  

#include<stdio.h>
#include<algorithm>
using namespace std;
#define maxn 200010
int f[maxn];
int sum[maxn];
int main(){
	int n,k;
	scanf("%d %d",&n,&k);
	for(int i=1;i<=n;i++){
		scanf("%d",&f[i]);
		f[i]=f[i]+f[i-1];
		if(i>=k){
			sum[i]=f[i]-f[i-k];
		}
	}
	if(2*k>=n){
		printf("%d\n",f[n]);
		return 0;
	}
	int ans=0;
	int maxz=sum[k];
	for(int i=2*k;i<=n;i++){
		if(sum[i]+maxz>ans){
			ans=sum[i]+maxz;
		}
		maxz=max(maxz,sum[i-k+1]);
	}
	printf("%d\n",ans);
}

G - FacePalm Accounting

 这题队友提供了一个思路然后我把它秒了  

题意:让改变的总和最小 然后使得所有长度为k的子串和都小于0      并且每个值不能小于序列的最小值(保证最小值为负数)

思路:我们从第一个长度为k的子串开始(1~k) 显然 如果这一段和不小0 我们改变的位置越靠后越好   然后再一次往后挪动一个位置  显然对于等于最小值得位置 我们不再更改  我们可以建立一个栈  一开始存1到k-1可更改的位置 然后当一个位置更改到最小值以后就把它弹出栈 当我们遍历到一个新的位置 就把它压入栈 这样就可以保证更新的位置尽量靠后了  因为答案一定存在 所以对于与当前位置大于k的位置 我们不需要弹出(如果不能保证有答案 我们需要弹出前面一些更改却没有效益的位置) 用树状数组去求和和更新即可

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+100;
typedef long long ll;
ll c[N];
int n,k;
void add(int x,ll val){
	while(x<=n){
		c[x]+=val;
		x+=x&-x;
	}
}
ll query(int x){
	ll ret = 0;
	while(x){
		ret+=c[x];
		x-=x&-x;
	}
	return ret;
}
ll a[N];
int st[N],tp;
int main(){
	scanf("%d%d",&n,&k);
	ll mi = 2e9;
	for(int i = 1; i <= n; i++) scanf("%lld",&a[i]),mi=min(a[i],mi),add(i,a[i]);
	for(int i = 1; i <= k-1; i++)
	if(a[i]!=mi) st[++tp]=i;
	ll ans = 0;
	for(int i = k; i <= n; i++){
		if(a[i]!=mi) st[++tp]=i;
		ll q;
		while((q=(query(i)-query(i-k)))>=0){
			//printf("tp=%d\n",tp);
			ll c = max(-q-1,mi-a[st[tp]]);
			//printf("q=%lld c=%lld\n",q,c);
			add(st[tp],max(-q-1,mi-a[st[tp]]));
			ans+=-(max(-q-1,mi-a[st[tp]]));
			a[st[tp]]+=max(-q-1,mi-a[st[tp]]);
			if(-q-1<=mi-a[i]) tp--;
		}
	}
	printf("%lld\n",ans);
	for(int i = 1; i <= n; i++)
	printf("%lld ",a[i]); 
	return 0;
} 

 

H - Minimal Agapov Code

I - Sale in GameStore

 水题就不说了

#include<stdio.h>
#include<algorithm>
using namespace std;
int p[2010];
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&p[i]);
	}
	sort(p+1,p+n+1);
	int ans=1;
	int now=0;
	for(int i=1;i<n;i++){
		if(now+p[i]<=p[n]){
			ans++;
			now=now+p[i];
		}
		else{
			break;
		}
	}
	printf("%d\n",ans);
}

J - Getting Ready for VIPC

K - Treeland

 题意:一颗树有n个结点 每个点按距离不递减的顺序给出一个排列 求树的结构

思路:构造题 以前还没做过这种构造一颗树的形态的  本来想了好久 刚想放弃的时候 突然想出了一个做法  我选取1为根结点  距离它最远的一定是叶子结点  然后我再找这个叶子结点的序列里面 离它最近的  这肯定是和这个叶子相连的边  放入答案集合 并且标记这条边  

然后我们再重复找叶子 并且删边的(删边就是计入答案集合并且标记 让新的叶子出来)

#include<bits/stdc++.h>
using namespace std;
#define pa pair<int,int>
vector<pa>ans;
map<pa,int>mp;
const int N = 2003;
int dis[N][N];
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		ans.clear();
		mp.clear();
		int n;
		scanf("%d",&n);
		for(int i = 1; i <= n; i++){
		 	for(int j = 1; j <= n; j++)
			scanf("%d",&dis[i][j]);
		}
		for(int i = 1; i <= n-1; i++){
			int ye = dis[1][n-i+1];
			for(int j = 2; j <= n; j++){
				if(mp[make_pair(dis[ye][j],ye)]==0){
					ans.push_back(make_pair(dis[ye][j],ye));
					mp[make_pair(dis[ye][j],ye)]=1;
					mp[make_pair(ye,dis[ye][j])]=1;
					break;
				}
			}
		} 
		for(auto v:ans){
			printf("%d %d\n",v.first,v.second);
		}
		puts("");
	}
	return 0;
}

L - Useful Roads

 

M - Variable Shadowing

 模拟题 不想多说 大力出奇迹

#include<bits/stdc++.h>
using namespace std;
int pos[2550][2];
char s[55][55];
int len[55];
char t[2550];
int match[2550],st[2550],tp;
int alst[29][2550],altp[29];
int main(){
	int n;
	scanf("%d",&n);
	int now = 0;
	getchar();
	for(int i = 1; i <= n; i++){
		gets(s[i]+1);
		len[i]=strlen(s[i]+1);
		for(int j = 1; j <= len[i]; j++){
			t[j+now]=s[i][j];
			//printf("i=%d j=%d j+now=%d\n",i,j,j+now);
			pos[j+now][0]=i;
			pos[j+now][1]=j;
		}
		now+=len[i];
	}
	//printf("%s\n",t+1);
	int lent=strlen(t+1);
	for(int i = 1; i <= lent; i++){
		if(t[i]=='{') st[++tp]=i;
		else if(t[i]=='}'){
			match[i]=st[tp];
			tp--;
		}
	}
	tp=0;
	for(int i = 1; i <= lent; i++){
		if(t[i]=='{'){
			for(int j = 0; j <= 25; j++)
				alst[j][++altp[j]]=i;
		}
		else if(t[i]=='}'){
			for(int j = 0; j <= 25; j++){
				while(alst[j][altp[j]]!=match[i]) 
				altp[j]--;
				altp[j]--;
			}
		}else if(t[i]==' ') continue;
		else{
			int c = t[i]-'a',laspos=0;
			for(int k = altp[c]; k >= 1; k--)
			if(t[alst[c][k]]==t[i]){
				laspos=alst[c][k];
				break;
			}
			if(laspos){
				printf("%d:%d: warning: shadowed declaration of %c, the shadowed position is %d:%d\n",pos[i][0],pos[i][1],t[i],pos[laspos][0],pos[laspos][1]);
			}
			alst[c][++altp[c]]=i;
		}
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值