传送门: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
枚举之后 设 为长度为 的 数组方案即可。
#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
先按 排序,用线段树记录所有没有后继的区间的 值。
每加入一段区间首先在线段树上二分查找最大的 的 ,判断接或不接的最小花费。
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,费用为
的边。
跑最大费用最大流即可。
#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
具体方法是先找到两个在不同根节点儿子子树中的叶节点,然后在它们之间的路径上 找根。
判断一个点
是否为叶结点:
随机选另一个点
,然后若所有其它点到
的路径都不经过
,则
为叶节点。复杂度为
找到一个叶结点:
随机选到叶结点的概率
,
越大,概率越高。20次找不到的概率低到了
。
找到另一个叶结点:随机选一个点判断在它与已知叶节点路径上点数是否为 。
找到根节点:直接 排入相对位置即可。
懒得写了,贴个标程
#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;
}