【Codeforces】Round #523 (Div. 2) A-F

版权声明:欢迎转载(请附带原链接)ヾ(๑╹◡╹)ノ" https://blog.csdn.net/corsica6/article/details/84502712

传送门:CF523Div2


A. Coins

贪心选尽量大的填

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int n,s,ans;

int main(){
	int i,j,mx;
    scanf("%d%d",&n,&s);
    for(i=min(n,s);i && s;--i) if(s>=i){
         ans+=s/i;s%=i;
	}
	printf("%d",ans);
}

B. Views Matter

离散化高度后,判断每两个高度间隔之间的列数,贪心先沿着对角线往后填,不够的强制填到这个高度的最后一列。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n,m,a[N],vs[N],op,mx;
ll ans,ss;

int main(){
	int i,j,k,mx=0,dc=0,dir=1;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;++i){
    	scanf("%d",&a[i]);ss+=a[i];
	}
	sort(a+1,a+n+1);
	mx=a[n];k=0;
    for(i=j=1;i<=n;i=j){
    	for(;j<=n && a[j]==a[i];++j);
    	dir+=min(n-dir+1,a[i]-dc);
    	ans+=a[i]-dc+max(0,j-dir);
		dc=a[i];dir=max(dir,j);
	}
	printf("%I64d",ss-ans);
    return 0;
}

C. Multiplicity

a i \sqrt a_i 枚举之后 d p dp f [ i ] f[i] 为长度为 i i b b 数组方案即可。

#include<bits/stdc++.h>
const int inf=1886417009;
using namespace std;
typedef long long ll;
const int N=510,M=1e6+10;

int n,m,inn,ott,val[N],S,T,vs[N],tim,sum;
int head[N<<1],to[M],nxt[M],w[M],cc[M],tot=1;
int cur[N<<1],dis[N<<1],stk[N],top;

inline void fl(){printf("-1");exit(0);}

inline void lk(int u,int v,int flw,int cst)
{
	to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=flw;cc[tot]=cst;
	to[++tot]=u;nxt[tot]=head[v];head[v]=tot;w[tot]=0;cc[tot]=-cst;
}

struct Gra{
	int head[N],to[N],nxt[N],tot,rt;
	int dmd[N],col[N],dl[N];
	
	inline void lk(int u,int v)
	{
	   to[++tot]=v;nxt[tot]=head[u];head[u]=tot;
	   to[++tot]=u;nxt[tot]=head[v];head[v]=tot;
	}
	
	void dfs(int x,int fa)
	{
		int i,j;
		for(i=head[x];i;i=nxt[i]){
			j=to[i];if(j==fa) continue;
			dfs(j,x);
		}
		stk[++top]=x;if(!dmd[x]) return;
		for(i=1;i<=top;++i) col[stk[i]]=x;
		for(i=1;i<top;++i) dl[x]-=dmd[stk[i]];
		if(dl[x]<0) fl();top=0;
	}
}A,B;

priority_queue<Pr>que;
inline bool spfa()
{
	memset(dis,0x8f,sizeof(dis));
	f
}

int main(){
	 int i,j,x,y,z;
	 scanf("%d%d%d",&n,&A.rt,&B.rt);S=(n<<1)+1;T=S+1;
	 for(i=1;i<=n;++i) scanf("%d",&val[i]);
	 for(i=1;i<n;++i){scanf("%d%d",&x,&y);A.lk(x,y);}
	 for(i=1;i<n;++i){scanf("%d%d",&x,&y);B.lk(x,y);}
	 for(scanf("%d",&z);z;--z) {scanf("%d%d",&x,&y);A.dmd[x]=A.dl[x]=y;}
	 for(scanf("%d",&z);z;--z) {scanf("%d%d",&x,&y);B.dmd[x]=B.dl[x]=y;}
	 A.dfs(A.rt,0);B.dfs(B.rt,0);
	 for(i=1;i<=n;++i) if(A.dl[i]) lk(S,i,A.dl[i],0),inn+=A.dl[i];
	 for(i=1;i<=n;++i) if(B.dl[i]) lk(i+n,T,B.dl[i],0),ott+=B.dl[i];
	 if(inn!=ott) fl();
	 for(i=1;i<=n;++i) lk(A.col[i],n+B.col[i],1,val[i]);
	 for(;spfa();)
	 	for(vs[T]=tim;vs[T]==tim;){
	 		++tim;memcpy(cur,head,sizeof(cur));inn-=dfs(S,inf);
	 	}
	 if(inn!=0) fl();
	 printf("%d",sum);
	 return 0;
}

D. TV Shows

先按 l l 排序,用线段树记录所有没有后继的区间的 r r 值。

每加入一段区间首先在线段树上二分查找最大的 l i \leq l_i r r ,判断接或不接的最小花费。

By ccosi, contest: Codeforces Round #523 (Div. 2), problem: (D) TV Shows, Accepted, #
 #include<bits/stdc++.h>
#define mid ((l+r)>>1)
#define lc k<<1
#define rc k<<1|1
using namespace std;
typedef long long ll;
const int N=1e5+10,mod=1e9+7;

int n,a,b,m,rv[N<<1],cnt,tot;
int ans,ss[N<<3];

struct P{
	int l,r;
	bool operator<(const P&ky)const{
	    return l<ky.l;
	}
}le[N];

struct Q{
	int v,id,sd;
	bool operator<(const Q&ky)const{
	   return v<ky.v;
	}
}q[N<<1];

int ask(int k,int l,int r,int R)
{
	if(!ss[k]) return 0;
	if(l==r) return l;int re=0;
	if(R>mid) re=ask(rc,mid+1,r,R);
	if(!re) re=ask(lc,l,mid,R);
	return re; 
}

void ad(int k,int l,int r,int pos,int vv)
{
	ss[k]+=vv;
	if(l==r) return;
	if(pos<=mid) ad(lc,l,mid,pos,vv);
	else ad(rc,mid+1,r,pos,vv);
}

int main(){
	int i,j,x,y;
	scanf("%d%d%d",&n,&a,&b);
	for(i=1;i<=n;++i){
		scanf("%d%d",&x,&y);
	    q[++cnt]=(Q){x,i,0};
	    q[++cnt]=(Q){y,i,1};
	}
	sort(q+1,q+cnt+1);
	for(i=1;i<=cnt;++i){
		if(q[i].v!=rv[tot]) rv[++tot]=q[i].v;
		if(q[i].sd) le[q[i].id].r=tot;
		else le[q[i].id].l=tot;
	}
	sort(le+1,le+n+1);
	for(i=1;i<=n;++i){
		j=0;
		if(le[i].l>1) j=ask(1,1,tot,le[i].l-1);
		if((!j) || a<=(ll)(rv[le[i].l]-rv[j])*b){
			ans+=(a+(ll)b*(rv[le[i].r]-rv[le[i].l])%mod)%mod;
		    ans%=mod;
			ad(1,1,tot,le[i].r,1);
		}else{
			ans+=(ll)b*(rv[le[i].r]-rv[j])%mod;
			ans%=mod;
			ad(1,1,tot,j,-1);ad(1,1,tot,le[i].r,1);
		}
	}
	printf("%d",ans);
	return 0;
}

E. Politics

最大费用最大流,设第一颗树中的点为 1 n 1-n ,第二颗树中的点为 ( n + 1 ) ( n + n ) (n+1)-(n+n) 。设 x i x_i 为对 i i 号节点的总数限制。

从源点向 i ( 1 i n ) i(1\leq i\leq n) 连一条流量为 x i j s u b i x j x_i-\sum \limits_{j\in sub_i}x_j ( j s u b i j\in sub_i 表示 j j i i 子树内且 j j i i 路径上没有其它有限制的点),费用为 0 0 的边。

i ( n + 1 i n + n ) i(n+1\leq i\leq n+n) 向汇点连一条流量为 x i j s u b i x j x_i-\sum\limits_{j\in sub_i}x_j ,费用为 0 0 的边。

c o l i col_i 表示深度最大的满足 i i 在其子树内的有限制结点(包括 i i 本身)。
c o l i col_i c o l i + n col_{i+n} 连一条流量为1,费用为 a i a_i 的边。

跑最大费用最大流即可。

#include<bits/stdc++.h>
const int inf=0x3f3f3f3f;
using namespace std;
typedef long long ll;
const int N=510,M=2e6+10;

int n,m,inn,ott,val[N],S,T,sum,rta,rtb,dmd[N<<1],col[N<<1];
int head[N<<1],to[M],nxt[M],w[M],cc[M],tot;
int dis[N<<1],pre[N<<1],bel[N<<1];bool inq[N<<1];

inline void fl(){printf("-1");exit(0);}

inline void lk(int u,int v,int flw,int cst)
{
	to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=flw;cc[tot]=cst;
	to[++tot]=u;nxt[tot]=head[v];head[v]=tot;w[tot]=0;cc[tot]=-cst;
}

int dfs(int x,int fa,int tpo)
{
	int i,j,re=0;if(dmd[x]) tpo=x;
	col[x]=tpo;
	for(i=head[x];i;i=nxt[i]) if(to[i]!=fa) 
	  re+=dfs(to[i],x,tpo);
	if(dmd[x]){i=dmd[x];dmd[x]-=re;if(dmd[x]<0) fl();re=i;}
	return re;
}

struct Pr{
  int id,v;
  bool operator<(const Pr&ky)const{
     return v<ky.v;
  }
}tp;

queue<int>que;
inline bool spfa()
{
	memset(dis,0x3f,sizeof(dis));
	int i,j,x;dis[S]=0;que.push(S);inq[S]=true;
	for(;que.size();){
		x=que.front();que.pop();
		for(i=head[x];i;i=nxt[i]){
			j=to[i];if((!w[i]) || dis[j]<=dis[x]+cc[i]) continue;
			dis[j]=dis[x]+cc[i];pre[j]=x;bel[j]=i;
			if(!inq[j]) que.push(j),inq[j]=true;
		} 
		inq[x]=false;
	}
	return (dis[T]<inf);
}

int main(){
	 int i,j,x,y,z;
	 scanf("%d%d%d",&n,&rta,&rtb);S=(n<<1)+1;T=S+1;rtb+=n;
	 for(i=1;i<=n;++i) scanf("%d",&val[i]);
	 for(i=1;i<n;++i){scanf("%d%d",&x,&y);lk(x,y,0,0);}
	 for(i=1;i<n;++i){scanf("%d%d",&x,&y);lk(x+n,y+n,0,0);}
	 for(scanf("%d",&z);z;--z) {scanf("%d%d",&x,&y);dmd[x]=y;}
	 for(scanf("%d",&z);z;--z) {scanf("%d%d",&x,&y);dmd[x+n]=y;}
	 dfs(rta,0,rta);dfs(rtb,0,rtb);
	 memset(head,0,sizeof(head));tot=1;
	 for(i=1;i<=n;++i) if(dmd[i]) lk(S,i,dmd[i],0),inn+=dmd[i];
	 for(i=n+1;i<=n+n;++i) if(dmd[i]) lk(i,T,dmd[i],0),ott+=dmd[i];
	 if(inn!=ott) fl();
	 for(i=1;i<=n;++i) 
	   lk(col[i],col[i+n],1,-val[i]);
	 z=inf;
	 for(;spfa();){
 	     for(x=T;x!=S;x=pre[x]) z=min(z,w[bel[x]]);
	     for(x=T;x!=S;x=pre[x])
		   w[bel[x]]-=z,w[bel[x]^1]+=z;
	     sum-=z*dis[T];inn-=z;
	 }
	 if(inn!=0) fl();
	 printf("%d",sum);
	 return 0;
}

F. Lost Root

具体方法是先找到两个在不同根节点儿子子树中的叶节点,然后在它们之间的路径上 h 2 h^2 找根。

判断一个点 i i 是否为叶结点:
随机选另一个点 j j ,然后若所有其它点到 j j 的路径都不经过 i i ,则 i i 为叶节点。复杂度为 O ( n ) O(n)

找到一个叶结点:
随机选到叶结点的概率 0.5 \geq 0.5 k k 越大,概率越高。20次找不到的概率低到了 1 2 20 \dfrac{1}{2^{20}}

找到另一个叶结点:随机选一个点判断在它与已知叶节点路径上点数是否为 2 h 1 2h-1

找到根节点:直接 h 2 h^2 排入相对位置即可。

懒得写了,贴个标程

#include <bits/stdc++.h>
using namespace std;
 
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
// #define endl "\n"
#define int long long

const int N=1e5+5;

int n, k, h, leaf1, leaf2;
int a[N];
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
vector<int> v, path;
map<tuple<int, int, int>, int> store;

int query(int a, int b, int c)
{
	tuple<int, int, int> cur=make_tuple(a, b, c);
	if(store.find(cur)!=store.end())
		return (store[cur]);
	cout<<"? "<<a<<" "<<b<<" "<<c<<endl;
	string s;
	cin>>s;
	return (store[cur] = ((s=="Yes")));
}

int print(int x)
{
    cerr<<x;
	cout<<"! "<<x;
	exit(0);
}

int findleaf()
{
	while(true)
	{
		shuffle(a+1, a+n+1, rng);
		int leaf=a[1];
		int random=a[2];
		bool check=1;
		for(int i=3;i<=n;i++)
		{
			check&=(query(random, leaf, a[i])==0);
			if(!check)
				break;
		}
		if(check)
			return leaf;
	}
}

int findleaf2()
{
	while(true)
	{
		int count=0;
		shuffle(a+1, a+n+1, rng);
		if(a[1]==leaf1)
			continue;
		int leaf=a[1];
		bool check=1;
		int cnt=0, reqd=2*(h-1) + 1;
		v.clear();
		for(int i=2;i<=n;i++)
		{
			if(a[i]==leaf1)
				continue;
			int current=query(leaf1, a[i], leaf);
			cnt+=current;
			if(current)
				v.push_back(a[i]);
		}
		if(cnt==reqd)
			return leaf;
	}
}

void findroot()
{
	path.push_back(leaf1);
	path.push_back(v[0]);
	path.push_back(leaf2);
	for(int i=1;i<v.size();i++)
	{
		vector<int> newpath;
		newpath.push_back(path[0]);
		for(int j=1;j<path.size();j++)
		{
			if(query(path[j-1], v[i], path[j]))
			{
				newpath.push_back(v[i]);
				for(int k=j;k<path.size();k++)
					newpath.push_back(path[k]);
				break;
			}
			else
				newpath.push_back(path[j]);
		}
		path=newpath;
	}
	print(path[h]);
}

int32_t main()
{
	cin>>n>>k;
	if(n==1)
	{
	    print(1);
	    return 0;
	}
	int cur=k;
	int nodes=1;
	h=0;
	while(nodes+cur<=n)
	{
		nodes+=cur;
		cur*=k;
		h++;
	}
	for(int i=1;i<=n;i++)
		a[i]=i;
	leaf1=findleaf();
	leaf2=findleaf2();
	findroot();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/84502712