Codeforces Global Round 1

D.

由于一组连续三个数的情况不会超过3个,因为如果超过3个的话,那么你可以直接归类到每个数取3个就行了

所以一个数被选中为组成连续三个数的情况不会超过6个,所以我们直接f[i][j][k]表示搜到i,且i剩余j个,(i-1)剩余k个的方案数,那么你就直接dp就行了

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+10;
int dp[MAXN][5][3];
bool b[MAXN][5][3];
int n,m,x;
int f[MAXN];
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&x);
		f[x]++;
	}
	for (int j=0;j<=min(f[1],4);j++) dp[1][j][0]=(f[1]-j)/3,b[1][j][0]=1;
	for (int i=1;i<m;i++)
	{
        for (int j=0;j<=4;j++)
        	for (int k=0;k<=2;k++)
        		if (b[i][j][k])
        		{
                   for (int s=0;s<=min(min(j,k),f[i+1]);s++)
                   	 {
                   	 	for (int la=0;la<=min(4,f[i+1]-s);la++)
                   	 		dp[i+1][la][min(j-s,2)]=max(dp[i+1][la][min(j-s,2)],dp[i][j][k]+s+(f[i+1]-s-la)/3),b[i+1][la][min(j-s,2)]=1;
                   	 }
        		}	
	}
	int ans=0;
	for (int j=0;j<=4;j++)
		for (int k=0;k<=2;k++)
			ans=max(ans,dp[m][j][k]);
	cout << ans << endl;	
}

E.

题意:给你一个目前已知的序列a,然后给你一个序列b,然后你有一种操作,可以把a_{i}=a_{i-1}+a_{i+1}-a_{i}(1<i<n),你可以随便执行该操作,问最后能否把a变成b

解析:我们考虑一下首尾两数是不会变的,所以我们就可以先把这个判掉

然后我们思考一下,如果我们知道这个数列的差分数列的话,那么这个数列也就确定了,所以我们把a的差分数列定位a',b的差分数列定为b',我们发现上述操作就是交换相邻两个差分数列中数的位置,所以直接排序

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+10;
int a[MAXN],b[MAXN]; 
int n;
int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=1;i<=n;i++) scanf("%d",&b[i]);
	if (a[1]!=b[1]||a[n]!=b[n]) {
		printf("%s\n","No");
	}
	else {
		for (int i=1;i<n;i++) a[i]=a[i+1]-a[i];
		for (int i=1;i<n;i++) b[i]=b[i+1]-b[i];
		sort(a+1,a+n);
		sort(b+1,b+n);
		for (int i=1;i<n;i++) if (a[i]!=b[i]) {
			printf("%s\n","No"); return 0;
		}	
		printf("%s\n","Yes");
	}
}

F.

题意:给你一棵树,每条边有一个权值,这棵树节点满足欧拉序标号,你有一系列询问让你找到与一直点v最近的叶子节点x的距离大小,l<=x<=r,

解析:那么我们思考一下,如果已经知道了每个叶子节点到根节点的距离,那么这个东西直接用线段树维护,那么我们思考一下,如果从这个点直接到他的儿子节点的话,路径权值修改的区间是连续的,那么我们可以直接记录子树的节点个数,然后直接离线加线段树维护就行了

#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+10;
const long long inf=1e17;
struct node{
	int u,to;
	long long w;
}edge[MAXN<<1];
struct lalala{
	int l,r,id;
};
struct data{
	long long min,po;
}tree[MAXN<<2];
vector<lalala>q[MAXN];
vector<pair<int,long long> >e[MAXN];
int head[MAXN],size[MAXN];
int n,x,l,r,v,m,k;
long long w;
long long ans[MAXN];
void add(int x,int y,long long z)
{
	edge[k].u=y;
	edge[k].to=head[x];
	edge[k].w=z;
	head[x]=k++;
}
void put(int l,int r,int k,int t,long long s)
{
	tree[k].min=min(tree[k].min,s);
	if (l==r) return;
	else {
		int mid=(l+r)/2;
		if (t<=mid) put(l,mid,k<<1,t,s); else put(mid+1,r,k<<1|1,t,s);
	}
}
int dfs(int now,long long len)
{
	bool p=0;
	size[now]=1;
	for (int i=0;i<e[now].size();i++)
	{
          int u=e[now][i].first;
          p=1;
          size[now]+=dfs(u,len+e[now][i].second);
	}
	if (!p) put(1,n,1,now,len);
	return size[now];
}
void update(int k)
{
	tree[k].min=min(tree[k<<1].min,tree[k<<1|1].min);
}
void down(int k)
{
	tree[k<<1].min+=tree[k].po; tree[k<<1].po+=tree[k].po;
	tree[k<<1|1].min+=tree[k].po; tree[k<<1|1].po+=tree[k].po;
	tree[k].po=0;
}
long long findans(int l,int r,int k,int ll,int rr)
{
	if (l==ll&&r==rr) return tree[k].min;
	else {
		int mid=(l+r)/2;
        down(k);
		if (rr<=mid) return findans(l,mid,k<<1,ll,rr);
		else if (ll>mid) return findans(mid+1,r,k<<1|1,ll,rr);
		else {
			return min(findans(l,mid,k<<1,ll,mid),findans(mid+1,r,k<<1|1,mid+1,rr));
		}
		update(k);
	}
}
void change(int l,int r,int k,int ll,int rr,long long s)
{
	if (l==ll&r==rr)
	{
		tree[k].po+=s; 
		tree[k].min+=s;
	}
	else {
		int mid=(l+r)/2;
		down(k);
		if (rr<=mid) change(l,mid,k<<1,ll,rr,s);
		else if (ll>mid) change(mid+1,r,k<<1|1,ll,rr,s);
		else {
			change(l,mid,k<<1,ll,mid,s); change(mid+1,r,k<<1|1,mid+1,rr,s);
		}
		update(k);
	}
}
void dfs1(int now)
{
      for (int i=0;i<q[now].size();i++)
      {
            ans[q[now][i].id]=findans(1,n,1,q[now][i].l,q[now][i].r);
      }
      int s=now;
      for (int i=0;i<e[now].size();i++)
      {
      	  int u=e[now][i].first;
          change(1,n,1,s+1,s+size[u],-e[now][i].second);
          change(1,n,1,1,s,e[now][i].second);
          if (s+size[u]+1<=n) change(1,n,1,s+size[u]+1,n,e[now][i].second);
          dfs1(u);
          change(1,n,1,s+1,s+size[u],e[now][i].second);
          change(1,n,1,1,s,-e[now][i].second);
          if (s+size[u]+1<=n) change(1,n,1,s+size[u]+1,n,-e[now][i].second);
          s+=size[u];
      }
}
int main()
{
	scanf("%d%d",&n,&m);
	memset(head,-1,sizeof(head));
	for (int i=2;i<=n;i++) 
	{
		scanf("%d%lld",&x,&w);
		e[x].push_back(make_pair(i,w));
	} 
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&v,&l,&r);
		lalala x;
		x.l=l; x.r=r; x.id=i;
		q[v].push_back(x);
	}
	for (int i=1;i<=(n<<2);i++) tree[i].min=inf;
	dfs(1,0);
    dfs1(1);
	for (int i=1;i<=m;i++) printf("%lld\n",ans[i]);
}

G.

这道题有点毒瘤

主要是分的情况数太多了

具体就不多说了,反正我们可以把白点引出一个三点链,最后把情况化为三种

1.如果有度>3的点显然可以

2.如果有度为3的两个点经过偶数条边

3.如果有一个度为3的点其两个子树中的点的个数>1

剩余情况就是平局

(这个后手方显然不会赢,真是不知道是谁出的这道题)

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+5e5+10;
const int MAX=5e5+10;
vector<int>e[MAXN];
int x,T,k,y,n,s1,s2;
bool p;
char s[MAX];
void add(int x,int y)
{
	e[x].push_back(y);
	e[y].push_back(x);
}
int dfs(int now,int fa,int dep){
    if (e[now].size()>3) p=1;
    if (e[now].size()==3) {
    	if (dep&1) s1++; else s2++;
    }
    int s=0;
    for (int i=0;i<e[now].size();i++)
    {
    	int u=e[now][i];
    	if (e[u].size()>=2) s++;
    	if (u==fa) continue;
        dfs(u,now,dep+1);
    }
    if (s>=2&&e[now].size()==3) p=1;
}
int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d",&n);
		s1=0; s2=0;
		for (int i=1;i<n;i++){
			scanf("%d%d",&x,&y); 
			e[x].push_back(y); e[y].push_back(x);
		}
		int sum=n;
		p=0;
		scanf("%s",s+1);
		for (int i=1;i<=n;i++) 
			if (s[i]=='W') 
			{
			  sum++;
              add(i,sum); 
              add(sum,sum+1); 
              sum++;
              add(sum-1,sum+1);
              sum++;
			} 
		dfs(1,0,0);	
		if (p||s1>=2||s2>=2) printf("%s\n","White"); else printf("%s\n","Draw");
		for (int i=1;i<=sum;i++) e[i].clear();
	}
}

 

H.

太难了我想必是不会的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值