zoukankan      html  css  js  c++  java
  • 2021.10.18模拟总结

    心路历程&&过程

    (CSP) 之前最大的一次翻车,很警醒。
    由于请了大概一周的病假,急于想拿高分。虽然也想着谨慎,但看来还是没稳住。

    过程

    • (13:40) 开始。
    • (14:00) 通读完毕。
    • (14:15) (T1) 打完部分分((40)),但没有充分验证。同时开始推 (T1) 的正解。
    • (15:15) 大概想出 (T1) 接近正解的做法,但尝试之后发现细节繁多实现复杂,没有把握短时间写完,转而看 (T2)
    • (15:45) 本来想 (T2) 暴力,但是想出正解,写完+验证完(埋下伏笔)。此时心情愉悦,感觉胜券在握。同时计划 (16:25) 写完 (T3) 部分分。
    • (16:25) 因为推着推着开始想 (T3) 正解,所以还没打部分分。此时不想放弃,继续推。
    • (16:40) 刚刚推完式子,打完之后 (WA) 了。此时想再推推。
    • (17:00) 检查出一些错误,但还是不对。觉得此时时间所剩不多((40min)),觉得再去打暴力此题必定没时间改,有点犹豫。
    • (17:40) 随着时间推移,更加不能去做其他的题(做了就相当于放任本题爆零),相信奇迹然而并没有发生。最终饮恨当场。

    赛后实际

    (0+60+10+0=70pts)
    基本把能遇到的问题都试了一遍。
    首先是 (T1) 由于部分分没有充分验证,而且最后心态有点急,所以多测清空不彻底+忘记打freopen
    (T2) 有点太得意忘形了,过程量忘开 (longlong) 爆掉。
    (T3) 的问题最大,部分分应该先打出来对着拍,剩下 (1h) 应该先写个暴力再回来看。最好是按照自己的计划来,先打暴力。
    (T4) 根本没做,,没什么可说的。

    问题

    • (T1) 用时略长,应该总用时 (70min) 左右换下脑子。
    • (T2) 不够仔细。
    • (T3) 没有按照计划做。

    题解

    T1 卡牌游戏

    经过一番思考之后发现 dfs 的复杂度就能过,关键在于怎么判断所有卡牌都确定。
    两种方法:

    • 方法1:对于两张不完全相同的牌,都存在一个提示能区分开它们,则说明所有卡牌均确定。
      感性理解就是牌能两两区分开,看似简单然而不好想。
    • 方法2:每次获得一个提示,相当于给对应的位置覆盖上一种标记。当每个位置的标记都不同时即可区分。
      具体实现通过二进制的唯一分解,标记分别记录为 (1,2,4,8,16,32...2^n) ,这样不同标记叠加产生的效果一定是不同的。

    代码

    点击查看代码
    
    #include<bits/stdc++.h>
    using namespace std;
    const int INF = 0x3f3f3f3f,N = 105;
    #define ll long long 
    inline ll read()
    {
    	ll ret=0;char ch=' ',c=getchar();
    	while(!(c>='0'&&c<='9')) ch=c,c=getchar();
    	while(c>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
    	return ch=='-'?-ret:ret;
    }
    int n,col[N],num[N];
    int ans=INF;
    bool vis[16];
    char s[N][4];
    inline bool check()
    {
    	for(int i=1;i<=n;i++)	
    		for(int j=i+1;j<=n;j++)	
    		{
    			if(col[i]==col[j]&&num[i]==num[j]) continue;
    			if(col[i]==col[j])
    			{
    				if(!vis[num[i]]&&!vis[num[j]]) return false;
    			}
    			else if(num[i]==num[j]) 
    			{
    				if(!vis[col[i]]&&!vis[col[j]]) return false;
    			}
    			else if(!vis[col[i]]&&!vis[col[j]]&&!vis[num[i]]&&!vis[num[j]]) return false;
    		}
    	return true;
    }
    void dfs(int stp,int cnt)//stp从1开始 
    {
    	if(check()) {ans=min(ans,cnt);return;}	
    	if(stp>14) return;
    	dfs(stp+1,cnt);
    	vis[stp]=1;
    	dfs(stp+1,cnt+1);
    	vis[stp]=0;
    }
    
    int main()
    {
    	int T=read();
    	while(T--)
    	{
    		n=read(); ans=INF;
    		for(int i=1;i<=n;i++) 
    			scanf("%s",s[i]+1),col[i]=s[i][1]-'A'+1,num[i]=s[i][2]-'0'+7;
    		//for(int i=1;i<=n;i++) printf("%d %d %d %d
    ",col[i],num[i],s[i][1],s[i][2]);
    		dfs(1,0);
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    /*
    2
    2
    A3 A3
    4
    A4 B4 B3 C3
    */
    

    T2 道路负荷

    考场切了,但因为 int 相乘爆 int 丢了 (40pts),是惨痛的教训。

    T3 矩阵填数

    整场考试崩盘的源泉。
    题解给的是容斥做法,不好理解,不好想,不好推,不好写,所以暂时放弃。
    隔壁 (mathtt{Hanoist}) 提供了一种易懂的方法。
    做法直接写在代码注释里了。

    代码

    点击查看代码
    
    #include<bits/stdc++.h>
    using namespace std;
    const int INF = 0x3f3f3f3f,mod = 998244353,N = 5005;
    #define ll long long
    inline ll read()
    {
    	ll ret=0;char ch=' ',c=getchar();
    	while(!(c>='0'&&c<='9')) ch=c,c=getchar();
    	while(c>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
    	return ch=='-'?-ret:ret;
    }
    int n;
    ll w[N];
    ll fac[N*N],ifac[N*N];
    ll dp[2][N];
    //dp[i][j]表示前i个数字总共放在j行的方案数,其中第一维用滚动数组滚掉了 
    ll qpow(ll x,ll y)
    {
    	ll ret=1LL;
    	while(y)
    	{
    		if(y&1) ret=ret*x%mod;
    		x=x*x%mod;
    		y>>=1;
    	}
    	return ret;
    }
    inline ll C(int n,int m){return fac[n]*ifac[m]%mod*ifac[n-m]%mod;}
    inline ll A(int n,int m){return fac[n]*ifac[n-m]%mod;}
    void deal_fac()
    {
    	fac[0]=fac[1]=1;
    	for(int i=2;i<=n*n;i++) fac[i]=fac[i-1]*i%mod;
    //	printf("fac[9]=%lld
    ",fac[9]);
    	ifac[n*n]=qpow(fac[n*n],mod-2);
    	for(int i=n*n-1;i>=0;i--) ifac[i]=ifac[i+1]*(i+1)%mod; 	
    }
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;i++) w[i]=read();
    	deal_fac();
    	ll ans=0LL;
    	dp[0][0]=1;//初始化 
    	for(int i=1;i<=n;i++)	
    	{
    		for(int j=1;j<=i;j++)
    		{
    			if(j>1) dp[1][j]=(dp[0][j-1]*n*j%mod+dp[0][j]*(j*n-i+1)%mod)%mod;
    			/*两个转移的情况分别是:
    				1:新放一行,那么这行相对于原本j-1行有j个位置,每行有n个位置,所以dp[0][j-1]*n*j
    				2:放在前面,那么前面本来有j*n个位置,放了(i-1)个数,所以是dp[0][j]*(j*n-i+1) */ 
    			else dp[1][j]=A(n,i);//第一行没法正常转移,相当于初始值(而且由于滚动数组没办法用别的方法初始化) 
    		}
    		for(int j=1;j<=i;j++) dp[0][j]=dp[1][j];//滚动到下一层 
    	}
    	for(int i=1;i<=n;i++) dp[1][i]=dp[1][i]*C(n,i)%mod;//乘上没考虑的组合数 
    	for(int i=1;i<=n;i++)	
    		ans=(ans+dp[1][i]*w[i]%mod*fac[n*n-n])%mod;//剩下的位置全排列 
    	printf("%lld
    ",ans);
    	return 0;
    }
    /*
    10
    8 8 6 6 4 5 11 17 21 44
    */
    

    T4 游走

    笑死,根本没做。
    题解高斯消元直接劝退。

  • 相关阅读:
    android代码签名和混乱的包装
    C# 中对WinForm窗体中的控件快速设置TableIndex次序
    常用的Oracle数据库语句 (待更新完毕)
    [转] C# 键盘中的按键对应的KeyValue
    Oracle 数据库中日期时间的插入操作
    C#操作Excel,对Sheet插入次序的控制 (有待完善)
    Simditor图片上传
    文件类似的推理 -- 超级本征值(super feature)
    leetcode 名单 Insertion Sort List
    汉高澳大利亚sinox2014电影播放flash最好的办法是安装游戏windows文本firefox
  • 原文地址:https://www.cnblogs.com/conprour/p/15419060.html
Copyright © 2011-2022 走看看