Codeforces Round #646 (Div. 2)(1363)解题报告A.思维 B.思维 C.(思维) D.(交互,先搁着) E(树上贪心) F(还没看)

对不起,丢人了Orz

A - Odd Selection

一开始奇数看错偶数,看错之后就开始慌了,疯狂WA之后心态就崩了Orz。
其实这道题这个数据范围已经降低要求了,只有1e3,算是给了一定的提示了。
首先,统计奇偶是基操。
很明显,这道题需要分类讨论:
若没有奇数,肯定不行,所以直接输出No
否则,我们枚举选择偶数的数量(因为一个数加上偶数之后,奇偶性不变,可以少考虑一些,也可以起到"充数"的效果)i(从多到少枚举),若(x-i)为奇数,可以直接break,剩下的x-i个数让奇数去填满。若奇数不够填,则No,否则Yes。
被惯性思维所困,只想到根据ji和ou去判断,但没想到这种操作需要注意很多细节Orz。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  
#include 
#define inf 0x3f3f3f3f
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
#define rep(i, a, n) for(register int i = a; i <= n; ++ i)
#define per(i, a, n) for(register int i = n; i >= a; -- i)
//#define ONLINE_JUDGE
using namespace std;
typedef long long ll;
const int mod=1e9+7;
template<typename T>void write(T x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
    {
        write(x/10);
    }
    putchar(x%10+'0');
}
 
template<typename T> void read(T &x)
{
    x = 0;char ch = getchar();ll f = 1;
    while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
    while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;};
ll ksm(ll a,ll n){//鐪嬫槸鍚﹁mod 
	ll ans=1;
	while(n){
		if(n&1) ans=(ans*a)%mod;
		a=a*a%mod;
		n>>=1;
	}
	return ans%mod;
}
//==============================================================
int t,n,x;


int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	#endif
	clock_t c1 = clock();
	//===========================================================
	read(t);
	  while(t--){
	    int ji=0,ou=0;
	    read(n),read(x);
	    rep(i,1,n){
	      int e;
	      read(e);
	      if(e%2) ji++;
	      else ou++;
	    }
	    if(ji==0){
	      puts("No");
	    }
	    else{
            per(i,0,ou){
                if((x-i)%2){
                    x-=i;
                    break;
                }
            }
            if(x%2&&x<=ji){
                puts("Yes");
            }
            else{
                puts("No");
            }
	    }
	  }
	//===========================================================
	std::cerr << "Time:" << clock() - c1 << "ms" << std::endl;
	return 0;
}

B - Subsequence Hate

害。。太菜了。。都比赛快结束才想出来。。。
题目中说"010"或"101"都不行,也就是说,符合条件的,其实只有类似000111111或者11110000这种0和1分别集中在两边的,那么,我们可以先前缀和处理下这个字符串,统计到每个位置位置,0出现的次数和1出现的次数。然后,枚举分界点,通过前缀和算出把前面全部变成0,后面全部变成1所需要操作的次数把前面的1全部变为0,全部0全部变为1的次数,两个取最小值作为ans[i],然后,取全体ans[i]的最小即可。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  
#include 
#define inf 0x3f3f3f3f
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
#define rep(i, a, n) for(register int i = a; i <= n; ++ i)
#define per(i, a, n) for(register int i = n; i >= a; -- i)
//#define ONLINE_JUDGE
using namespace std;
typedef long long ll;
const int mod=1e9+7;
template<typename T>void write(T x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
    {
        write(x/10);
    }
    putchar(x%10+'0');
}
 
template<typename T> void read(T &x)
{
    x = 0;char ch = getchar();ll f = 1;
    while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
    while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;};
ll ksm(ll a,ll n){//看是否要mod 
	ll ans=1;
	while(n){
		if(n&1) ans=(ans*a)%mod;
		a=a*a%mod;
		n>>=1;
	}
	return ans%mod;
}
//==============================================================
const int maxn=1e3+10;
int t;
string str;
int s1[maxn];
int s2[maxn];
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	#endif
	clock_t c1 = clock();
	//===========================================================
	read(t);
    while(t--){
        memset(s1,0,sizeof(s1));
        memset(s2,0,sizeof(s2));
        cin>>str;
        int n=str.length()-1;
        int cnt1=0,cnt0=0;
        rep(i,0,str.length()-1){
            if(i==0){
                s1[i]=(str[i]=='0');
                s2[i]=(str[i]=='1');
            }
            else{
                s1[i]=s1[i-1]+(str[i]=='0');
                s2[i]=s2[i-1]+(str[i]=='1');
            }
        }
        int ans=inf;
        rep(i,0,str.length()-1){
            ans=min(ans,min(s1[i]+s2[n]-s2[i],s2[i]+s1[n]-s1[i]));
        }
        write(ans),putchar('\n');
    }
	//===========================================================
	std::cerr << "Time:" << clock() - c1 << "ms" << std::endl;
	return 0;
}

C - Game On Leaves

这题不算很难,想清楚的话。。
若一开始,x就已经是叶子节点,那当然是谁先出手谁就赢了。
若x一开始并不是叶子节点,那么,若有一个人让他变成了叶子节点,那这个人必输,因此,最优的出手应该是一直保持着x节点有两条边连着,那么,肯定先摘掉其他的所有节点,直到只剩下三个(x本身+两个和他相连的),这时候,谁先出手,谁就输,所以,判断下n-3的次数,算出剩下三个节点时谁先出手,另一方就赢了。
再衍生一下:n-3的奇偶性和n是相反的,所以,直接判断n奇偶性就行了。
因此,总结下:算下x点的度,如果x是根节点直接输出。否则,根据n的奇偶性判断。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  
#include 
#define inf 0x3f3f3f3f
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
#define rep(i, a, n) for(register int i = a; i <= n; ++ i)
#define per(i, a, n) for(register int i = n; i >= a; -- i)
//#define ONLINE_JUDGE
using namespace std;
typedef long long ll;
const int mod=1e9+7;
template<typename T>void write(T x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
    {
        write(x/10);
    }
    putchar(x%10+'0');
}
 
template<typename T> void read(T &x)
{
    x = 0;char ch = getchar();ll f = 1;
    while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
    while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;};
ll ksm(ll a,ll n){//看是否要mod 
	ll ans=1;
	while(n){
		if(n&1) ans=(ans*a)%mod;
		a=a*a%mod;
		n>>=1;
	}
	return ans%mod;
}
//==============================================================
const int maxn=1e3+10;
int deg[maxn];
int t;

int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	#endif
	clock_t c1 = clock();
	//===========================================================
	read(t);
    while(t--){
        memset(deg,0,sizeof(deg));
        int n,x;
        read(n),read(x);
        rep(i,1,n-1){
            int a,b;
            read(a),read(b);
            deg[a]++;
            deg[b]++;
        }
        if(deg[x]==1||deg[x]==0){
            puts("Ayush");
            continue;
        }
        if((n-2)%2==0){
            puts("Ayush");
        }
        else{
            puts("Ashish");
        }
    }
	//===========================================================
	std::cerr << "Time:" << clock() - c1 << "ms" << std::endl;
	return 0;
}

E - Tree Shuffling

这道题。。还是得借助大佬题解了Orz
这道题说的好像有点恐怖,但是稍微整理一下就容易很多了。
每个节点有当前状态b[i]和目标状态c[i](b[i]到c[i]是0到1之间的互换),我们能做的操作是在一个子树中,以根节点价值*交换的节点数的代价,交换这些节点的当前状态,最终令整棵树的所有节点达到目标状态。我们会发现:对于任何一个子树,他们交换的代价,可以是(子树的根节点或其祖先的价值)交换次数,根据贪心原则,(子树的根节点或其祖先的价值)肯定选最小的,所以,我们可以有这样的思路:
从根节点(1)开始dfs,每次统计每个以每个节点为根的子树中需要0->1和1->0的节点个数。统计之后,用节点及其祖先中最小的花费a[i]去交换该子树中全部能在该子树中完成交换的节点数(其实就是2
min(0->1的数量,1->0的数量)),这样,就能用最小的花费完成这些交换。
PS:为什么不是回溯到上面的几级节点再交换呢?因为深度越深,可选择的a[i]更多,更可能能找到小的。
记得更新下还是需要交换的节点的数量。具体看代码。

include <iostream>
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  
#include 
#define inf 0x3f3f3f3f
#define AC return 0;
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
#define rep(i, a, n) for(register int i = a; i <= n; ++ i)
#define per(i, a, n) for(register int i = n; i >= a; -- i)
#define ONLINE_JUDGE
using namespace std;
typedef long long ll;
const int mod=1e9+7;
template<typename T>void write(T x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
    {
        write(x/10);
    }
    putchar(x%10+'0');
}
 
template<typename T> void read(T &x)
{
    x = 0;char ch = getchar();ll f = 1;
    while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
    while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;};
ll ksm(ll a,ll n){//看是否要mod 
	ll ans=1;
	while(n){
		if(n&1) ans=(ans*a)%mod;
		a=a*a%mod;
		n>>=1;
	}
	return ans%mod;
}
//==============================================================
const int maxn=2e5+10;
int a[maxn],b[maxn],c[maxn];
ll ztoo[maxn],otoz[maxn];
ll dp[maxn];
int n;
struct Edge{
    int to,next;
}e[maxn<<1];
int cnt,head[maxn];
void add(int x,int y){
    e[cnt].to=y;
    e[cnt].next=head[x];
    head[x]=cnt++;
}
ll ans=0;
void dfs(int u,int fa,int mc){
    ll tmp=0;
    if(b[u]!=c[u]){//加入当前节点的情况
        if(b[u]==0){
            ztoo[u]=1;
        }
        else{
            otoz[u]=1;
        }
    }
    for(int i=head[u];~i;i=e[i].next){
        int v=e[i].to;
        if(v==fa) continue;
        dfs(v,u,min(mc,a[v]));//将祖先中最小的a[i]传下去
        //更新当前子树的情况
        otoz[u]+=otoz[v];
        ztoo[u]+=ztoo[v];
    }
    ans+=mc*min(otoz[u],ztoo[u])*2;//用最小的花费交换,更新答案
    int dec=min(otoz[u],ztoo[u]);//更新仍然需要交换的节点数
    otoz[u]-=dec;
    ztoo[u]-=dec;
}


int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	#endif
	clock_t c1 = clock();
	//===========================================================
	read(n);
    memset(head,-1,sizeof(head));
    rep(i,1,n) read(a[i]),read(b[i]),read(c[i]);
    rep(i,1,n-1){
        int x,y;
        read(x),read(y);
        add(x,y),add(y,x);
    }
    dfs(1,-1,a[1]);
    if(otoz[1]||ztoo[1]){//若到了根节点依然没法交换完,说明无法完成交换,输出-1
        write(-1);
    }
    else{
        write(ans);
    }
	//===========================================================
	std::cerr << "Time:" << clock() - c1 << "ms" << std::endl;
	AC
}

你可能感兴趣的:(cf)