2023.10.14 做题记录

2023.10.14 做题记录

P5595 歌唱比赛

一个非常简单的贪心。

先判断什么时候是 -1 ,将字符串从头开始往后遍历,Z 的右边不能有 X,Y,如果有则直接输出 -1

因为是 SPJ,如果该字符串有答案的话,倒着看,字母是谁的就随便给一个大的数,如果是 \(X\),则小\(X\)的数为 \(5\),小\(Y\)的数为 \(4\),其余情况同理。

CF1428B

若一个房间左边或右边是 -,则其必符合条件,用 \(cnt1\) 来统计。

若一个房间左右都是 <,用 \(cnt2\) 统计。

若一个房间左右都是 >,用 \(cnt3\) 统计。

>< 都存在时,\(2\)\(3\) 类房间都不符合条件,答案为 \(cnt1\)

< 存在且 > 不存在时,\(2\) 类房间符合条件,答案为 \(cnt1 + cnt2\)

> 存在且 < 不存在时,\(3\) 类房间符合条件,答案为 \(cnt1 + cnt3\)

CF743C

\(\frac{2}{n}\) 分为 \(\frac{1}{n}\)+\(\frac{1}{n}\)

再根据裂项公式 $\frac{1}{n}$-$\frac{1}{n+1}$=$\frac{1}{n(n-1)}$ 导出 $\frac{1}{n}$=$\frac{1}{n+1}$+$\frac{1}{n(n-1)}$

所以 $\frac{2}{n}$=$\frac{1}{n}$+$\frac{1}{n+1}$+$\frac{1}{n(n-1)}$

然后 $\frac{1}{x}$+$\frac{1}{y}$+$\frac{1}{z}$=$\frac{1}{n}$+$\frac{1}{n+1}$+$\frac{1}{n(n-1)}$

于是 $x=n,y=n+1,z=n(n-1$)

CF476D

猜结论!

观察两组样例,可以发现共同点

  • 样例一输出的是 1,2,3,5
  • 样例二输出结果中最小的四个数是 2,4,6,10

不难发现,二者具有二倍关系,二者二倍关系刚好是 \(k\)

猜出结论!

cin >> n >> k;
cout << (6 * n - 1) * k << endl;
for (rint i = 1; i <= n; i++)
{
	int x = 6 * i - 5;
	cout << x * k << " " << (x + 1) * k << " " << (x + 2) * k << " " << (x + 4) * k << endl;
}

CF1430C

继续猜结论,一直从右往左删即可,最小值为 \(2\)

因为删到最后状态一定剩两个数,由于不可能剩两个 \(1\),所以最小值为 \(2\)

我们可以将两个数相加除以二看作将两个数消成一个数,那要让剩下的数最小,就应该先去消大的,所以从右边开始消即可。

CF1025C

第一眼平衡树,第二眼区间 dp,第三眼大水题

先化环为链,然后求环上的最长交错字段,扫一遍就完事儿了,最后对串的长度除以二。

NOIP 2022 喵了个喵

首先观察数据范围,发现卡牌种类数 $k\in\lbrace2n−2,2n−1\rbrace$,说明 $k$ 与栈数量 $n$ 之间有种神秘的关系。

先看 $k=2n-2$ 时的情形,发现 $2n-2=2(n-1)$,即每个栈分配两个不同的数,仍能留下一个空栈,把这个空栈记为 $sp$。
我们还注意到如果多于两个牌存在于一个栈中,中间的元素会比较尴尬,不好消掉,经常会造成无解情况。

于是有一种思路便呼之欲出了

每次从牌堆处理一张牌时,如果这张牌的图案在某个栈的顶部出现,那么对该栈进行操作 $1$ 并消掉两张牌;
如果这张牌的图案在某个栈 $p_i$ 的底部出现,那么对 $sp$ 栈进行操作 $1$,再对 $p_i$ 和 $sp$ 进行操作 $2$,消掉两张牌;
否则对任意一个非 $sp$ 的栈进行操作 $1$ 即可。

由于只有 $2n-2$ 种图案,这样的方法可以保证得出结果。可以骗到前 $15$ 分

15 pts代码在后面。


再看 $k=2n-1$ 时的情形,这时上面的策略就不好用了,因为无法保证让 $sp$ 一直为空且让所有其他栈内不超过三张牌。

那么怎么解决呢?

任意时刻,设牌堆顶的牌为 $u$。保留一个栈为空,记其为 $sp$。

策略 1:
条件:存在 $sp$,且 $u$ 的同类牌在场上存在 或 非 $sp$ 栈中存在至少一个栈大小不超过 $1$

  • 除了 $sp$ 外,其他的栈都至多放 $2$ 个元素。每次处理一张牌时,如果 $u$ 在某个栈 $P$ 的顶部出现过,那么把 $u$ 放入 $P$;如果 $u$ 在某个栈 $P$ 的底部出现过,那么把 $u$ 放入 $sp$,对 $P$ 和 $sp$ 进行操作 $2$;否则把 $u$ 放入某个未满的非 $sp$ 栈里。

策略 2:
条件:存在 $sp$,且 $u$ 的同类牌在场上不存在 且 非 $sp$ 栈中没有栈大小不超过 $1$

  • 记当前所有在栈顶的 $n-1$ 个元素构成集合 $S$。我们不断扫描之后的图案,直到遇到第一个不在 $S$ 中的图案为止。
  • 如果这个图案就是 $u$:把牌堆顶的 $u$ 和现在的 $u$ 都放入 $sp$,消掉它们。因为中间的所有元素都是栈顶,可以直接放入和它们对应的栈上。
  • 如果这个图案不是 $u$:那么这个图案 $v$ 是某个栈 $P$ 的栈底。记 $P$ 的栈顶为 $w$,讨论 $w$ 在刚刚扫描过的所有图案中出现次数的奇偶性:
    • 奇数次:把 $u$ 放入 $sp$,所有的 $w$ 放入 $P$。这样 $v$ 再次出现的时候,$P$ 上面的 $w$ 被清空了,此时可以直接把 $v$ 消掉,$P$ 变成新的 $sp$。
      • 这个过程中,其他栈顶元素放入对应栈顶即可。
    • 偶数次:把 $u$ 放入 $P$,所有的 $w$ 放入 $sp$(这里放到 $P$ 里也行)。这样 $v$ 再次出现的时候,$P$ 中自顶到底分别是 $u,w,v$ 三个元素。把 $v$ 放入 $sp$,再消掉 $P$ 和 $sp$ 的栈底,这样 $P$ 中仍然只有两个元素。
      • 这个过程中,其他栈顶元素放入对应栈顶即可。
      • 关于操作限制,不用担心,因为无论哪种策略,最后都不可能操作超过 \(m\) 次。

        代码如下:

        #include <bits/stdc++.h>
        
        #define rint register int
        #define endl '\n'
        
        using namespace std;
        
        const int N = 2e6 + 5;
        const int M = 3e3 + 5;
        
        int T;
        int n, m, k;
        int a[N];
        deque<int> stk[M];// 维护每个栈的栈底和栈顶的元素
        int id[N]; // 维护所在栈的编号,若局面未出现则为 0
        int num; // 辅助栈的编号
        queue<int> q;// 用于维护哪些栈可以弹入元素。初始时除了辅助栈,每个栈可以弹入 2 次
        vector<pair<int, int>> ans; // 保存答案
        
        int read()
        { 
            int x = 0;
            bool f = true;
            char ch = getchar();
            for(; !isdigit(ch); ch = getchar())
                if(ch == '-')
                    f = false;
            for(; isdigit(ch); ch = getchar())
                x = (x << 1) + (x << 3) + ch - '0';
            return f ? x : (~(x - 1));
        }
        
        void insert(int s, int x) // 操作 1,向 s 中插入 x
        { 
            if (!stk[s].empty() && stk[s].back() == x) 
        	{
                stk[s].pop_back();// 若顶部两个元素相同则弹出
            } 
        	else 
        	{
                stk[s].push_back(x);// 否则弹入
            }
        
            ans.emplace_back(s, -1);// 添加入答案
        }
        
        void del(int s1, int s2) // 操作 2,消除 s1 和 s2 的栈底
        { 
            stk[s1].pop_front();// 弹出栈底
            stk[s2].pop_front();// 弹出栈底
            ans.emplace_back(s1, s2);// 添加入答案
        }
        
        bool simple(int x) // 新出来一个数 x,尝试简单相消
        { 
            int &s = id[x];
        
            if (!s) // 若 x 未出现过
        	{ 
                if (q.empty()) // 没有可弹入的栈,return false
        		{ 
                    return false;
                }
                s = q.front();
                q.pop();
                insert(s, x); // 否则弹入下一个可弹入的栈
            } 
        	else 
        	{
                q.push(s);// 若 x 出现过,一定能够消除,把原先 x 所在的位置弹入 q
        
                if (x == stk[s].back()) // 在栈顶
        		{ 
                    insert(s, x);
                } 
        		else // 在栈底
        		{ 
                    insert(num, x);
                    del(num, s);
                }
        
                s = 0; // 然后改为局面未出现
            }
        
            return true;
        }
        
        signed main() 
        {
            cin >> T;
        
            while (T--) 
        	{
                cin >> n >> m >> k;
        
                for (rint i = 1; i <= m; i++) 
        		{
                    a[i] = read();
                }
        
                ans.clear();
                memset(id, 0, sizeof id);
                num = n;
        
                while (!q.empty()) 
        		{
                    q.pop();
                }
        
                for (int i = 1; i < n; i++) 
        		{
                    q.push(i);
                    q.push(i);
                }
        
                for (rint i = 1; i <= m; i++)
        		 {
                    if (!simple(a[i]))  // 如果尝试简单相消行不通
        			{
                        int u = a[i];// 记录当前数值
                        int r = i + 1;
        				int v = a[r];
        
                        while (r <= m && v != u && stk[id[v]].back() == v)
                        {
                            v = a[++r];		// 寻找下一个不在栈顶的数			
        				}// 此时,r 是 i 后第一个不在栈顶的下标,v 是 a[r]
        
                        if (v == u) // 如果 v 正好和 u 一样
        				{
                            insert(num, u);
        
                            for (rint j = i + 1; j < r; j++)
                            {
                                simple(a[j]);						
        					}
                            insert(num, v);// 把他们在辅助栈消除
                        } 
        				else // 否则 v 是某个栈 P 的栈底
        				{
                            int p = id[v];
        					int w = stk[p].back();// 记 P 的栈顶为 w
                            bool is_even = true;// 查看 w 出现次数奇偶性
                            for (rint j = i + 1; j < r; j++)
                            {
                                if (a[j] == w)
                                {
                                    is_even = !is_even;								
        						}				
        					}
        
                            if (is_even)  // w 出现次数为偶数
        					{
                                insert(p, u);// u 放入 P
        
                                for (rint j = i + 1; j < r; j++) 
        						{
                                    if (a[j] == w)
                                    {
                                        insert(num, w);								
        							}
                                    else
                                    {
                                        simple(a[j]);								
        							}
                                }
                                insert(num, v);
                                del(num, p);// 消除两个栈底的 v
                                id[v] = 0;
                                id[u] = p;
                            } 
        					else 
        					{
                                insert(num, u);
        
                                for (rint j = i + 1; j < r; j++) 
        						{
                                    if (a[j] == w)
                                    {
                                        insert(p, w);								
        							}
                                    else
                                    {
                                        simple(a[j]);								
        							}
                                }
                                insert(p, v);
                                id[v] = id[w] = 0;
                                id[u] = num;
                                q.push(num);
                                num = p;
                            }
                        }
                        i = r;
                    }
                }
        
                printf("%d\n", (int)ans.size());
        
                for (rint i = 0; i < (int)ans.size(); i++) 
        		{
                    if (ans[i].second == -1)
                    {
                        printf("1 %d\n", ans[i].first);				
        			}
                    else
                    {
                        printf("2 %d %d\n", ans[i].first, ans[i].second);				
        			}
                }
            }
        
            return 0;
        }
        

        部分分 15pts 代码:

        #include <bits/stdc++.h>
        
        #define rint register int
        #define endl '\n'
        
        using namespace std;
        
        const int N = 1e6 + 5;
        
        int T;
        int n, m, k;
        int a[N];
        deque<int> st[5000];
        int id[N], spt;
        queue<int> q;
        vector<pair<int, int> > ans;
        
        void insert(int s, int x)
        {
            if(!st[s].empty() && st[s].back() == x) 
        	{
        		st[s].pop_back();
        	}
            else 
        	{
        		st[s].push_back(x);
        	}
            ans.emplace_back(s, -1);
        }
        
        void del(int s1, int s2)
        {
            st[s1].pop_front();
            st[s2].pop_front();
            ans.emplace_back(s1, s2);
        }
        
        void simple(int x)
        {
            int &s = id[x];
            if(!s)
        	{
                if(q.empty())
        		{
                    puts("-1");
                    exit(0);
                }
                s = q.front(); 
        		q.pop();
                insert(s, x);
            }
        	else
        	{
                q.push(s);
                if(x == st[s].back())
        		{
                    insert(s, x);
                }
        		else
        		{
                    insert(spt, x);
                    del(spt, s);
                }
                s = 0;
            }
        }
        
        signed main()
        {
            cin >> T;
            
            while(T--)
        	{
                cin >> n >> m >> k;
                
                for(rint i = 1; i <= m; i++)
                {
        			cin >> a[i];
        		}
        		
                ans.clear();
                memset(id, 0, sizeof(id));
                spt = n;
        
                while(!q.empty()) 
        		{
        			q.pop();
        		}
        		
                for(rint i = 1; i < n; i++)
        		{
                    q.push(i);
                    q.push(i);
                }
        
                for(rint i = 1; i <= m; i++)
                {
                    simple(a[i]);			
        		}
        
                cout << ans.size() << endl;
                
                for(rint i = 0; i < ans.size(); i++)
        		{
                    if(ans[i].second == -1) 
        			{
        				printf("1 %d\n", ans[i].first);
        			}
                    else 
        			{
        				printf("2 %d %d\n", ans[i].first, ans[i].second);
        			}
                }
            }
            return 0;
        }
        
posted @ 2023-10-14 17:30  PassName  阅读(11)  评论(0编辑  收藏  举报