1. 程式人生 > >【Codeforces】 Educational Round 54 Div. 2 A-G

【Codeforces】 Educational Round 54 Div. 2 A-G

傳送門:cf1076


A. Minimizing the String

貪心刪去第一個字典序大於後一個位置的字母的位置。

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

int n;
char s[200005];

int main(){
	int i,j;
	scanf("%d%s",&n,s+1);
    for(i=1;i<n;++i)
     if(s[i]>s[i+1]) break;
    for(j=1;j<=n;++j)
     if(i!=j) putchar(s[j]);
    return 0;
}

B.Divisor Subtraction

特殊性質:
偶數次數 n/2
奇數 (n-n的最小質因數)/2+1

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;

ll n,ans;
int p[N],tot;
bool pri[N];

inline void pre()
{
	int i,j,res;
	for(i=2;i<N;++i){
		if(!pri[i]) p[++tot]=i;
		for(j=1;j<=tot && (ll)i*p[j]<N;++j){
			res=i*p[j];pri[res]=true;
			if(i%p[j]==0) break;
		}
	}
}

void sol(ll n)
{
	int i;
	if(!(n&1)) ans=n>>1;
	else{
		for(i=1;i<=tot;++i) if(n%p[i]==0) break;
	    if(i<=tot){
	       ans=1+(n-p[i])/2;
		}else{
			ans=1;
		}
	} 
	 
 }

int main(){
	int i,j;pre();
	scanf("%I64d",&n);
	sol(n);
	printf("%I64d",ans);
    return 0;
}

C.Meme Problem

解個二元一次方程。

#include<bits/stdc++.h>
using namespace std;
#define db double

int t,d;

int main(){
	db a,b,c,g,ans;
	scanf("%d",&t);
	for(;t;--t){
		scanf("%d",&d);
		a=1.0;b=-(db)d;c=-b;
		g=sqrt(b*b-4*c);
		ans=(-b+g)/2.0;
		if(ans>=0.0 && ans<=(db)d){
			printf("Y %.10lf %.10lf\n",ans,d-ans);
		}else{
			ans=(-b-g)/2.0;
			if(ans>=0.0 && ans<=(db)d)
			 printf("Y %.10lf %.10lf\n",ans,d-ans);
		    else printf("N\n");
		}
	}
	return 0;
}

D.Edge Deletion

再也不寫SPFA了!!!
跑個最短路樹再貪心取

By ccosi, contest: Educational Codeforces Round 54 (Rated for Div. 2), problem: (D) Edge Deletion, Accepted, #
 #include<bits/stdc++.h>
using namespace std;
const int N=3e5+100;
typedef long long ll;

int n,m,K,f[N],sz[N],ans[N],cot;
int head[N],to[N<<1],nxt[N<<1],w[N<<1],tot;
ll dis[N],inf;

struct P{
    int u;ll d;
    bool operator<(const P&ky)const{
	     return ky.d<d;
	}
}temp;

struct LE{
	int u,v,w;
	bool operator<(const LE&ky)const{
		if(u!=ky.u) return u<ky.u;
		if(v!=ky.v) return v<ky.v;
	   return w<ky.w;
	}
};

map<LE,int>mp;
priority_queue<P>que;

inline void lk(int u,int v,int vv)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=vv;}

inline void spfa()
{
	int i,j,x,y;
	memset(dis,0x7f,sizeof(ll)*(n+3));
	inf=dis[1];dis[1]=0LL;que.push((P){1,0});
	for(;que.size();){
		temp=que.top();que.pop();x=temp.u;
		if(dis[x]!=temp.d) continue;
		for(i=head[x];i;i=nxt[i]){
			j=to[i];if(dis[j]<=dis[x]+w[i]) continue;
			dis[j]=dis[x]+w[i];
			que.push((P){j,dis[j]});
		}
	}
}

int getfa(int x){return x==f[x]?x:(f[x]=getfa(f[x]));}

void ck(int x,int fr)
{
	for(int j,i=head[x];i;i=nxt[i]){
		j=to[i];if(j==fr) continue;
		printf("%d ",w[i]);ck(j,x);
	} 
}

bool inq[N];
queue<int>Q;

int main(){
	int i,j,x,y,z,pr;
	scanf("%d%d%d",&n,&m,&K);
	for(i=1;i<=m;++i){
		scanf("%d%d%d",&x,&y,&z);
		mp[(LE){x,y,z}]=i;mp[(LE){y,x,z}]=i;
		lk(x,y,z);lk(y,x,z);
	}
	spfa();
	Q.push(1);inq[1]=1;
	for(;Q.size();){
	    if(cot==K) break;
		x=Q.front();Q.pop();
	    for(i=head[x];i;i=nxt[i]){
	    	j=to[i];if(dis[j]>=inf || inq[j] || dis[j]!=dis[x]+w[i]) continue;
	    	cot++;
			ans[cot]=mp[(LE){x,j,w[i]}];
			Q.push(j);inq[j]=1;
			if(cot==K) break; 
		}
	} 
	printf("%d\n",cot);
	for(i=1;i<=cot;++i) printf("%d ",ans[i]);
	return 0;
} 

E.Vasya and a Tree

將每個結點轉成平面上的一個點的深度看做 y y 座標, d f s dfs 序為 x x 座標,每次操作相當於一個矩陣區域的加值。最終查詢矩陣每個點的值。
離線下來線段樹維護即可。

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
typedef long long ll;

int n,m,dep[N],df[N],ot[N],dfn,cnt;
int head[N],to[N<<1],nxt[N<<1],tot;
ll ss,ans[N],bit[N];

struct P{
	int r,c,v;
	P(int r_=0,int c_=0,int v_=0):r(r_),c(c_),v(v_){};
	bool operator <(const P&ky)const{
	     return r<ky.r;
	}
}q[N<<2],p[N];

inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}

inline void ad(int x,ll v)
{for(;x<=n;x+=(x&(-x))) bit[x]+=v;}
inline ll ask(int x)
{
	ll re=0LL;
	for(;x;x-=(x&(-x))) re+=bit[x];
	return re;
}

void dfs(int x,int fa)
{
	df[x]=++dfn;
	for(int j,i=head[x];i;i=nxt[i]){
		j=to[i];if(j==fa) continue;
		dep[j]=dep[x]+1;dfs(j,x);
	}
	ot[x]=dfn;
}

int main(){
	int i,j,x,y,z;
	scanf("%d",&n);
	for(i=1;i<n;++i){
		scanf("%d%d",&x,&y);
		lk(x,y);lk(y,x);
	}
	dep[1]=1;dfs(1,0);
	scanf("%d",&m);
	for(i=1;i<=m;++i){
		scanf("%d%d%d",&x,&y,&z);
		q[++cnt]=P(dep[x],df[x],z);
		if(ot[x]<n){
		q[++cnt]=P(dep[x]+y+1,ot[x]+1,z),
		q[++cnt]=P(dep[x],ot[x]+1,-z);	
		}
		q[++cnt]=P(dep[x]+y+1,df[x],-z);
	}
	sort(q+1,q+cnt+1);
	for(i=1;i<=n;++i) p[i]=P(dep[i],df[i],i);
	sort(p+1,p+n+1);
	
	for(i=j=z=1;z<=n;i=j){
	    for(;j<=cnt && q[j].r==p[z].c;++j) 
		  ad(q[j].c,q[j].v);
	    for(;z<=n && p[z].r<=q[i].r;++z) 
	     ans[p[z].v]=ask(p[z].c);
	}
	for(i=1;i<=n;++i) printf("%I64d ",ans[i]);
	return 0;	
}

F.Summer Practice Report

兩次貪心取。
一次貪心 T T 後跟 k k F F 。判每次剩下的 F F 的個數。
一次貪心 F F 後跟 k k T T 。判每次剩下的 T T 的個數。
若兩次都合法,就有解。

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
typedef long long ll;

int n,K,a[N],b[N];
ll f[N];

int main(){
	int i,j,la,ra,lb,rb,x,y,pr=0;
	scanf("%d%d",&n,&K);
	for(i=1;i<=n;++i) scanf("%d",&a[i]);
	for(i=1;i<=n;++i) scanf("%d",&b[i]);
	for(i=1;i<=n;++i){
		f[i]=max(0LL,f[i-1]+a[i]-(ll)K*b[i]);
		if(f[i]>K) {puts("NO");return 0;}
	}
	for(i=1;i<=n;++i){
		f[i]=max(0LL,f[i-1]+b[i]-(ll)K*a[i]);
		if(f[i]>K) {puts("NO");return 0;}
	}
	puts("YES");
}

G.Array Game

考慮單組詢問的做法,設 d p [ i ] [ j ] dp[i][j] 為當前在 b i b_i 處,且 b i = j b_i=j 時的狀態, 1 1 表示必勝, 0 0 表示必敗。

顯然答案與 j j 的具體值無關,只與 j j 的奇偶性有關, d p [ i ] [ j ] dp[i][j] 的第二維 j j 可以省略,只需要記錄每個點 D P i = d p [ i ] [ b i   x o r   1 ] DP_i=dp[i][b_i\ xor \ 1] 的值。

D P i = ( b i   x o r 1 ) ( D P i + 1   x o r   1 ) ( D P i + 2   x o r   1 ) . . . ( D P i + m   x o r   1 ) DP_i=(b_i\ xor 1)|(DP_{i+1}\ xor \ 1)|(DP_{i+2}\ xor \ 1)...(DP_{i+m}\ xor\ 1)

  1. m m 個位置中有一個位置 j j 滿足 d p [ j ] [ b j 1 ] = 0 dp[j][b_j-1]=0 ,則當前狀態必勝。
  2. 後繼狀態全為必勝,則只有 b i b_i 為偶數( b i   x o r   1 b_i\ xor \ 1 為奇數)時,能夠達到必勝狀態。

於是從後往前 d p dp ,得到了一個 O ( n m ) O(nm) 的做法。

區間操作顯然要上顆線段樹,發現只需要記錄一個關鍵資訊“後繼狀態中最近的必敗狀態和當前位置之間的距離 d i s dis ”,於是考慮對於每個結點構造一個函式 g [ i ] g[i] ,分別表示初始狀態 d i s dis i i 時,倒序 d p dp 轉移完該結點管轄區間後的狀態的 d i s dis
這樣每次就可以 O ( m ) O(m) 合併了。

考慮對於單個點的初始化:
g [ i ] = i + 1 ( 1 i m ) g[i]=i+1(1\leq i\leq m) (情況1.)
g [ m + 1 ] g[m+1] 表示後繼狀態全為必勝的狀態。
b i b_i 為奇數時, g [ m + 1 ] = 0 g[m+1]=0
b i b_i 為偶數時, g [ m + 1 ] = 1 g[m+1]=1

對於區間加值的操作:偶數之間忽略,奇數則區間取反。所以需要每個結點維護區間全部值取反後的 g g&#x27; 函式,區間加操作轉換成了區間 s w a p ( g , g ) swap(g,g&#x27;) ,打個 l a z y t a g lazytag 後標記永久化即可。

#include<bits/stdc++.h>
#define mid ((l+r)>>1)
#define lc k<<1
#define rc k<<1|1
using namespace std;
const int N=2e5+20;
typedef long long ll;

int n,m,q,rv[N<<2];ll a[N];

struct node{
	int g[2][6];
	inline void itia(int op){
		for(int i=0;i<m;++i) g[0][i]=g[1][i]=i+1;
		g[0][m]=m;g[1][m]=0;
		if(op) g[0][m]=0,g[1][m]=m;
    }
    inline void flp()
    {for(int i=0;i<=m;++i) swap(g[0][i],g[1][i]);}
}t[N<<2],temp;

inline node mg(node a,node b)
{
	node re;int i,j;
	for(i=0;i<2;++i)
	 for(j=0;j<=m;++j)
	  re.g[i][j]=a.g[i][b.g[i][j]];
	return re;
}

inline void build(int k,int l,int r)
{
	if(l==r) {t[k].itia((int)(a[l]&1));return;}
	build(lc,l,mid);build(rc,mid+1,r);
	t[k]=mg(t[lc],t[rc]);
}

inline void cg(int k,int l,int r,int L,int R)
{
	if(L<=l && r<=R) {rv[k]^=1;t[k].flp();return;}
    if(L<=mid) cg(lc,l,mid,L,R);
    if(R>mid) cg(rc,mid+1,r,L,R);
    t[k]=mg(t[lc],t[rc]);
    if(rv[k]) t[k].flp();
}

inline node ask(int k,int l,int r,int L,int R)
{
	if(L<=l && r<=R) return t[k];
	node re;
	if(R<=mid) re=ask(lc,l,mid,L,R);
	else if(L>mid) re=ask(rc,mid+1,r,L,R);
	else re=mg(ask(lc,l,mid,L,R),ask(rc,mid+1,r,L,R));
	if(rv[k]) re.flp();
	return re;
}

int main(){
	int i,j,op,l,r;ll z;
	scanf("%d%d%d",&n,&m,&q);
	for(i=1;i<=n;++i) scanf("%I64d",&a[i]);
	build(1,1,n);
	for(;q;--q){
		scanf("%d%d%d",&op,&l,&r);
		if(op==1){
			scanf("%I64d",&z);
			if(z & 1) cg(1,1,n,l,r);
		}else puts(ask(1,1,n,l,r).g[0][m]?"1":"2");
	}
	return 0;
}