• [CEOI2019]Cubeword(暴力)


    没错,标签就是暴力。

    首先发现棱上的所有词长度都相等,枚举长度 (len)

    然后发现这些词中只有第一个字符和最后一个字符比较重要(只有这两个位置会与别的串衔接,中间的是啥无所谓)。

    (cnt_{i,j}) 为第一个字符是 (i),最后一个字符是 (j) 的词的个数。注意的是一个串反过来也算一个合法的串(题意说了正反读都行),但是如果这个反串也在输入中被给出了那就不重复算了。

    容易想到 (O(|Sigma|^8len)) 的最暴力做法:枚举八个顶点。

    下面还有六次方五次方的部分分,略过。直接讲 (O(|Sigma|^4len)) 的正解。

    假如这个立方体是 (ABCD-A'B'C'D'),枚举 (A,C,B',D') 上的字符。对于每个剩下的顶点,与它相邻的三个顶点已经被枚举到了。

    问题就是快速求每个剩下的顶点的方案数。

    再计算 (tot_{i,j,k}) 为相邻三个字符是 (i,j,k) 的方案数。也可以四次方解决。

    这样常数不够优秀,注意到预处理时 (i,j,k) 时顺序没啥大关系,计算时 (A,C,B',D') 的顺序也没啥大关系(乘个系数就好了),所以可以有个 (frac{1}{6}) 的常数。

    那就做完了。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=100010,mod=998244353,val[2][2][2]={{{24,12},{12,4}},{{12,6},{4,1}}};
    #define lson o<<1,l,mid
    #define rson o<<1|1,mid+1,r
    #define FOR(i,a,b) for(int i=(a);i<=(b);i++)
    #define ROF(i,a,b) for(int i=(a);i>=(b);i--)
    #define MEM(x,v) memset(x,v,sizeof(x))
    inline int read(){
    	int x=0,f=0;char ch=getchar();
    	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    	return f?-x:x;
    }
    int n,cnt[62][62],tot[62][62][62],l[maxn],ans;
    char s[maxn][13];
    bool pali[maxn];
    map<string,bool> vis;
    inline int id(char c){
    	if(c>='a' && c<='z') return c-'a';
    	if(c>='A' && c<='Z') return c-'A'+26;
    	if(c>='0' && c<='9') return c-'0'+52;
    }
    int main(){
    	n=read();
    	FOR(i,1,n){
    		scanf("%s",s[i]);
    		l[i]=strlen(s[i]);
    		pali[i]=true; 
    		FOR(j,0,l[i]-1) if(s[i][j]!=s[i][l[i]-1-j]) pali[i]=false;
    	}
    	FOR(len,3,10){
    		MEM(cnt,0);MEM(tot,0);
    		vis.clear();
    		int upr=0;
    		FOR(i,1,n) if(l[i]==len){
    			if(vis[string(s[i])]) continue;
    			string tmp=string(s[i]);
    			reverse(tmp.begin(),tmp.end());
    			vis[tmp]=true;
    			cnt[id(s[i][0])][id(s[i][l[i]-1])]++;
    			if(!pali[i]) cnt[id(s[i][l[i]-1])][id(s[i][0])]++;
    			upr=max(upr,id(s[i][0]));
    			upr=max(upr,id(s[i][l[i]-1]));
    		}
    		FOR(i,0,upr) FOR(j,i,upr) FOR(k,j,upr) FOR(l,0,upr)
    			tot[i][j][k]=(tot[i][j][k]+1ll*cnt[i][l]*cnt[j][l]%mod*cnt[k][l])%mod;
    		FOR(i,0,upr) FOR(j,i,upr) FOR(k,j,upr) FOR(l,k,upr){
    			int s=1ll*tot[i][j][k]*tot[i][j][l]%mod*tot[i][k][l]%mod*tot[j][k][l]%mod,hhh=0;
    			ans=(ans+1ll*s*val[i==j][j==k][k==l])%mod;
    		}
    	}
    	printf("%d
    ",ans);
    }
    
  • 相关阅读:
    Silverlight Toolkit ListBoxDragDropTarget学习笔记
    函数指针和指针函数(转)
    面试题_反转链表
    C++中的异或运算符^
    面试题_旋转字符串
    面试题_寻找丑数
    模拟一个简单的基于tcp的远程关机程序
    管理指针成员
    赫夫曼树编码问题
    堆的基本操作
  • 原文地址:https://www.cnblogs.com/1000Suns/p/11737508.html
Copyright © 2020-2023  润新知