• CF gym-102452 2019-2020 ICPC Asia Hong Kong Regional Contest


    B. Binary Tree

    给你一棵树,两个人每次只能移走一颗子树,这个子树必须是满二叉树(叶子也行),第一个人先开始,谁不能操作谁就输了,问你谁能赢。

    满二叉树是奇数个节点((2^x-1)),那么每次都是移走奇数个点,判奇偶性就行了。

    D. Defining Labels

    进制转换,把给的数的10进制转化为带限制k进制,每一位都要在原本认知的基础上-1。

    以k=10为例:

    x = 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 (十进制)
    0 1 2 3 4 5 6 7 8 9 00 01 02 03 04 (十进制每一位还要-1)

    如果要满足每一位-1,上面9和10是对应的,如果对每一位-1,那么10第一位-1就要把第二位退一位下来;如果先转10进制再模拟减法退位应该是可行的,代码量也应该不会很大。

    还有一种做法是:

    void solve(int k, int x)
    {
        if (!x)return;
        x--;
        solve(k, x / k);
        ans.push_back((x % k) + 10 - k);
    }
    

    假设x=114514,k=5,我们虽然不知道x的5进制表示,但如果x的五进制表示的最后一位为0时,把x--之后它的五进制也自动进行了退位,不为0时就是最后一位-1,这样x%k得到的就是最后一位的位权,那么再把这个x/=k,相当于把x右移一位,这样递归下去直到x==0时就把x分解完了w

    最后再加上题目条件10-k即可。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 3e5 + 10;
    #define ll long long
    #define pii pair<int,int>
    #define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
    
    ll mod = 1e9 + 7;
    
    vector<int>ans;
    
    void solve(int k, int x)
    {
        if (!x)return;
        x--;
        solve(k, x / k);
        ans.push_back((x % k) + 10 - k);
    }
    
    int main()
    {
        fastio;
        int t;
        cin >> t;
        while (t--)
        {
            ans.clear();
            ll k, x;
            cin >> k >> x;
            solve(k, x);
            for (auto i : ans)
                cout << i;
            cout << endl;
        }
        return 0;
    }
    

    E. Erasing Numbers

    给长度为n的排列,每次操作选择连续的3个数,然后删除最大的和最小的数,进行很多次最终留下一个数。问你这个排列中哪些数可以在进行某一系列操作之后留下来。

    记当前的数为x,比它大的数变为1,比它小的数变为0,比较0和1的个数:

    ①如果0和1的个数相同,那么肯定可以让这个数最终留下来(给出一种示例操作:由于0和1的个数相同,那么每次操作最理想的就是移除一个0一个1。出现0x1和1x0时移除x两侧的0和1;剩下的情况全部枚举出来:000、111、011、100、。。。操作一个000时肯定要对应的操作一个111,不过大部分时候连续0周围都会有1,这样就可以满足移除一个0一个1,连续1同理)

    ②如果0和1的个数不同,那么就要把多出来的数删除。由于只存在两种删除模式(删除2个相同的数(3个连续相同的数可以这样)和各删除一个),很显然只能去寻找删除2个相同的数这种情况。

    让cnt0和cnt1做差,如果为奇数就凑不出来;

    否则就再里面找连续的去删,看看能不能删到0和1的数量相同的状态,再去用各减1的操作处理。

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
    const int inf = 1e9 + 7;
    const ll lnf = 1e18 + 7;
    const int maxn = 1e6 + 10;
    ll mod = 998244353;
    
    bool ans[maxn];
    
    int n;
    
    int solve(int x,vector<int>&a)
    {
    	int cnt = 0, tot = 0;
    	for (int i = 1; i <= n; i++)
    		if (a[i] > a[x])cnt++;
    		else if (a[i] < a[x])tot++;
    	if (cnt == tot)return 1;
    	if (abs(cnt - tot) & 1)return 0;
    	int flag = (cnt > tot ? 1 : -1);
    	int sum = 0;
    	for (int i = 1; i <= n; i++)
    	{
    		if (i == x)
    		{
    			sum = 0;
    			continue;
    		}
    		if (a[i] > a[x])
    			sum += flag;
    		else sum -= flag;
    		if (sum < 0)
    			sum = 0;
    		if (sum == 3)
    		{
    			if (flag == 1)
    				cnt -= 2;
    			else tot -= 2;
    			sum = 1;
    			if (cnt == tot)
    				return 1;
    		}
    	}
    	return 0;
    }
    
    int main()
    {
    	fastio;
    	int t;
    	cin >> t;
    	while (t--)
    	{
    		cin >> n;
    		vector<int>a(n + 1);
    		for (int i = 1; i <= n; i++)
    			cin >> a[i], ans[i] = 0;
    		for (int i = 1; i <= n; i++)
    			ans[i] = solve(i, a);
    		for (int i = 1; i <= n; i++)
    			cout << ans[i] << "";
    		cout << endl;
    	}
    	return 0;
    
    }
    

    G. Game Design

    构造一棵树,每个点可以安排一个权值,选择它获得权值并覆盖它可以到达的所有叶子节点。需要构造的这棵树有k种权值和最小的选择方式选择一些点覆盖所有的叶子节点。

    在纸上画画可以发现一个规律,对于两棵树,把他们连向一个构造根,操作数相乘,当这个根的权值等于两棵树所有叶子节点的权值和时操作数再+1,否则不加。(说简单一点就是子树合并满足乘法原理,连向的根需要权值特判)

    可以想到一个数可以被这种方式分解:(117 = 116 + 1 = (58 * 2) + 1 =(29 * 2 * 2) + 1 = ((28 + 1) * 2 * 2) + 1) .......

    每次+1可以构造新根连向上一个构造根,*2可以把一个大小为2的链连向当前构造根。

    最后把叶子节点的所有权值赋为1,跑一遍dfs统计一下各个非叶子节点的权值。

    如果k一开始就是偶数那么这棵树的root可以赋个无限大权值就不用-1了。

    由于这个数只有1e9,大概质因数分解也可以构造,k更大一些出现大质数就不能质因数分解了。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 3e5 + 10;
    #define ll long long
    #define pii pair<int,int>
    #define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
    
    ll mod = 1e9 + 7;
    int c[maxn], fa[maxn];
    
    vector<int>G[maxn];
    
    int dfs(int from)
    {
        int res = G[from].empty();
        for (auto to : G[from])
            res+=dfs(to);
        return c[from] = res;
    }
    
    int main()
    {
        fastio;
        int k;
        cin >> k;
        int K = k;
        if (k == 1)
        {
            cout << 2 << endl;
            cout << 1 << endl;
            cout << 10000 << " " << 1 << endl;
            return 0;
        }
        c[1] = (k & 1 ? 1 : 1e8);
        if (k & 1)
            k--;
        int cnt = 1;
        int last = 1;
        int tot = 1;
        while (k > 1)
        {
            while (k % 2 == 0)
            {
                ++cnt;
                G[last].push_back(cnt);
                fa[cnt] = last;
                ++cnt;
                G[cnt - 1].push_back(cnt);
                fa[cnt] = cnt - 1;
                k /= 2;
            }
            if (k == 1)
                break;
            k--;
            ++cnt;
            G[last].push_back(cnt);
            fa[cnt] = last;
            last = cnt;
        }
        dfs(1);
        if (K % 2 == 0)
            c[1] = 1e8;
        cout << cnt << endl;
        for (int i = 2; i <= cnt; i++)
            cout << fa[i] << " ";
        cout << endl;
        for (int i = 1; i <= cnt; i++)
            cout << c[i] << " ";
        return 0; 
    
    }
    

    J. Junior Mathematician

    让你求L到R区间内满足条件的在模m意义下的x同余f(x)的x的个数。

    f(x)的定义:

    这个式子含义就是:累加每一位的贡献,它的贡献就是它的权值乘上len到上一位的前缀和。

    以下为设计状态的过程,WA+TLE了一上午

    很显然是数位dp,最开始想dp[pos][limit][][],后两维表示pos到len所有位的和、所有位累加的f(x),然后发现记录前缀和相同的状态它们所对应的前面一段x对m取模可能不同,故又加上一维,然后爆空间了。

    然后发现(f(x)equiv x(mod m))可以变成(f(x)-x equiv 0(mod m)),这样后面又只需要两维:对于当前位置pos,第一维是pos到len这一段x对m取模的权值,第二维是pos到len这一段f(x)-x的权值(要+m %m弄成正的)。
    第一维我尝试去用

    (X * 10 + i) % m
    

    去取前缀模,发现是错的,因为这种取模方式得到的结果要跑完整个长度才是正确的(后面很多个*10都没有累乘到前面去取模)

    需要先去打一个表,把每一位需要乘多少个10记录下来:

    		for (int i = 1; i < max(s.length(), p.length()); i++)
    			biao[i] = biao[i - 1] * 10 % m;
    

    导致TLE35的原因之一,计算多了,形参多了:

    最后就是ac代码,这个题目卡常,不该取模的地方不要取模,函数的形参能少开就少开:

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
    const int inf = 1e9 + 7;
    const ll lnf = 1e18 + 7;
    const int maxn = 1e6 + 10;
    int mod = 1e9 + 7;
    
    int m;
    
    int dp[5005][65][65];
    
    int biao[5050];
    
    int len = 0, b[5050];
    
    int dfs(int pos, int flag, int res, int sum)
    {
    	if (pos == -1)
    		return res == 0;
    	if (!flag && ~dp[pos][sum][res])return dp[pos][sum][res];
    	int r = flag ? b[pos] : 9;
    	ll tmp = 0;
    	for (int i = 0; i <= r; i++)
    		tmp += dfs(pos - 1, flag && i == b[pos], ((res + sum * i - i * biao[pos]) % m + m) % m, (sum + i) % m);
    	tmp %= mod;
    	if (!flag)
    		return dp[pos][sum][res] = tmp;
    	else 
    		return tmp;
    }
    
    void init(int len)
    {
    	//memset(dp, -1, sizeof(dp));
    	for (int i = 0; i < len; i++)
    			for (int k = 0; k < m; k++)
    				for (int l = 0; l < m; l++)
    					dp[i][k][l] = -1;
    }
    
    int main()
    {
    	fastio;
    	int t;
    	cin >> t;
    	biao[0] = 1;
    	while (t--)
    	{
    		string s, p;
    		cin >> s >> p >> m;
    		for (int i = 1; i < max(s.length(), p.length()); i++)
    			biao[i] = biao[i - 1] * 10 % m;
    		int ok = 0;
    		for (int i = s.length() - 1; i >= 0; i--)
    		{
    			if (s[i] == '0')
    				s[i] = '9';
    			else
    			{
    				s[i] --;
    				break;
    			}
    		}
    		if (s[0] == '0')ok = 1;
    		len = 0;
    		init(max(s.length(), p.length()));
    		for (int i = s.length() - 1; i >= ok; i--)
    			b[len++] = s[i] - '0';
    		int res1 = dfs(len - 1, 1, 0, 0);
    		len = 0;
    		for (int i = p.length() - 1; i >= 0; i--)
    			b[len++] = p[i] - '0';
    		int res2 = dfs(len - 1, 1, 0, 0);
    		cout << (res2 - res1 + mod) % mod << "
    ";
    	}
    	return 0;
    
    }
    
  • 相关阅读:
    前端开发笔记(2)css基础(上)
    前端开发笔记(1)html基础
    <Android 基础(三 十)> Fragment (3) ~ PreferenceFragment
    MyEclipse中搭建Struts2开发环境
    Android 投射工具和录屏工具
    似是而非的k=sqrt(n)
    算法:求比指定数大且最小的“不重复数”问题的高效实现
    巧用“异或”
    12个滑稽的C语言面试问答——《12个有趣的C语言问答》评析(5)
    12个滑稽的C语言面试问答——《12个有趣的C语言问答》评析(4)
  • 原文地址:https://www.cnblogs.com/ruanbaiQAQ/p/14659829.html
Copyright © 2020-2023  润新知