A
方法一:
考虑观察,能否放置只和当前放置块的下表面和底座的上表面是否契合有关。所以我们考虑记录每个块的所有下表面,发现我们可以用前一个位置和后一个的差来唯一的形容下表面。所以我们对底座做差分,然后对当前方块的四个面的差分序列进行搜索。本题数据范围较小,可以使用暴力搜索。但观察其实是字符串匹配问题,也可以优化到线性。
方法二:
考虑用二进制描述方块和底座的关系,我们每次不断下降方块,直到当前的方块再下降就会和底座重合。然后对下降结束的完整情况进行查询,是否有悬空的 \(1\),没有即为合法。
int sz[13]={0,1,2,1,1,2,2,1,1,3,2,2,2};
int su[8]={2,1,2,2,4,4,4};
int a[13][3]={{0},{0},{0,0},{1},{-1},{-1,0},{0,1},{2},{-2},{0,0,0},{-1,1},{1,0},{0,-1}};
int us[8][4]={{0,9},{1},{6,4},{5,3},{2,3,4,10},{8,2,1,11},{7,2,1,12}};
int c[105],n,d[105],p;
inline int fnd(int x){
int res=0;
rep(i,1,n-1){
int flag=1;
rep(j,0,sz[x]-1){
if(i+j>=n||a[x][j]!=d[i+j])flag=0;
}
res+=flag;
}
if(x==0)res=n;
return res;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>p;
p--;
rp(i,n)cin>>c[i];
rp(i,n-1)d[i]=c[i+1]-c[i];
int ans=0;
rd(i,su[p]){
ans+=fnd(us[p][i]);
}
cout<<ans<<endl;
return 0;
}
//Shimny_Rain_Z
B
方法一:
考虑枚举我们需要的两张牌,然后发现所有的手牌组存在全序关系,也就可以在两个人的可行组合中分别选出最大的,直接比较两者即可。而最大的可以通过枚举全部可能再意义对比选出。所以我们只需要实现比较两副手牌的大小。这个部分可以通过模拟来实现,实现方法各有不同。枚举则有深搜、二进制、循环、全排列等多种写法。
方法二:
为了提高效率,我们考虑直接找出在给定的七张牌中选出的最优的五张。所以我们就从高到低,依次查找当前的牌组能否选出大小为 \(5\) 的子集满足条件,如同花顺,\(4+1\) 等等。然后我们通过编码的形式将牌组之间的全序关系映射到编码的全序关系。经过一系列复杂的讨论得到答案。
map<int,int>mp;
struct card{
int num,col;
int hsh(){
return (num<<2|col);
}
card(){
num=0,col=0;
}
card(int x){
col=(x&3),num=(x>>2);
}
card(int c,int n){
num=n,col=c;
}//12*4+2
card(string &s){
if(s[0]=='S')col=0;
else if(s[0]=='D')col=1;
else if(s[0]=='C')col=2;
else col=3;
if(isdigit(s[1]))num=s[1]-'0'-2;
else if(s[1]=='T')num=8;
else if(s[1]=='J')num=9;
else if(s[1]=='Q')num=10;
else if(s[1]=='K')num=11;
else if(s[1]=='A')num=12;
}
}my[2],op[2],co[3],hi[2];
int cnt[13];
vt<int>vc[4],vs,vn;
inline int calc(vt<int>&v){
//´Ó¸ßÍùµÍÕÒ˳×Ó
if(v.size()<5)return -1;
per(i,4,(int)v.size()-1){
bool flag=1;
rep(j,i-4,i-1)if(v[j]+1!=v[j+1])flag=0;
if(flag)return v[i];
}
if(v.back()!=12)return -1;
if(v[0]!=0)return -1;
rd(i,3)if(v[i]+1!=v[i+1])return -1;
return 3;
}
inline int find(vt<int>&v){
if(v.size()<5)return -1;
int res=0;
per(i,(int)v.size()-5,(int)v.size()-1){
res=res*15+v[i];
}return res;
}
inline pii solve(vt<card>&v){
rd(i,4)vc[i].clear();
rep(i,0,12)cnt[i]=0;vs.clear();
for(auto i:v)vc[i.col].pb(i.num),vs.pb(i.num);
rd(i,4)sort(vc[i].begin(),vc[i].end());
sort(vs.begin(),vs.end());vn=vs;
vn.erase(unique(vn.begin(),vn.end()),vn.end());
for(auto i:vs)cnt[i]++;
//ͬ»¨Ë³£º´Óͬ»¨ÐòÁÐÖÐÌáÈ¡³ö¹«²îΪ1£¬ÏîÊýΪ5µÄµÈ²îÊýÁÐ
int res=calc(vc[0]);
rp(i,3)res=max(res,calc(vc[i]));
if(res!=-1)return {9,res};
//4+1£ºÕÒ³öÒ»¸ö¶«Î÷ÓÐËĸö£¬ÔÙÕÒ³öÒ»¸ö¶«Î÷ÓÐÒ»¸ö
per(i,0,12)if(cnt[i]>=4)per(j,0,12)if(j!=i&&cnt[j]){
int res=i*15+j;
return {8,res};
}
//3+2£ºÕÒ³öÒ»¸ö¶«Î÷ÓÐÈý¸ö£¬ÔÙÕÒ³öÒ»¸ö¶«Î÷ÓÐÁ½¸ö
per(i,0,12)if(cnt[i]>=3)per(j,0,12)if(cnt[j]>=2&&j!=i){
int res=i*15+j;
return {7,res};
}
//´Óͬ»¨ÐòÁÐÖÐÌáÈ¡³öÇ°Îå´óµÄ£¬ ¹þÏ£³ÉÊý
res=find(vc[0]);
rp(i,3)res=max(res,find(vc[i]));
if(res!=-1)return {6,res};
//˳×Ó£º´ÓÆÕͨÐòÁÐÖÐÌáÈ¡³ö¹«²îΪ1£¬ÏîÊýΪ5µÄµÈ²îÊýÁÐ
res=calc(vn);
if(res!=-1)return {5,res};
//3+1+1£ºÕÒ³öÒ»¸ö¶«Î÷ÓÐÈý¸ö£¬È»ºóÕÒ³öÒ»¸öºÍÒ»¸ö
per(i,0,12)if(cnt[i]>=3){
per(j,0,12)if(j!=i&&cnt[j])per(k,0,j-1)if(k!=i&&cnt[k]){
int res=i*15*15+j*15+k;
return {4,res};
}
}
//2+2+1£ºÕÒ³öÒ»¸ö¶«Î÷ÓÐÁ½¸ö£¬ÔÙÕÒ³öÒ»¸ö¶«Î÷ÓÐÁ½¸ö£¬È»ºóÓÃÊ£ÏÂ×î´óµÄ
per(i,0,12)if(cnt[i]>=2)per(j,0,i-1)if(cnt[j]>=2){
per(k,0,12)if(k!=i&&k!=j&&cnt[k]){
int res=i*15*15+j*15+k;
return {3,res};
}
}
//2+1+1+1£ºÕÒ³öÒ»¸ö¶«Î÷ÓÐÁ½¸ö£¬È»ºóÓÃÊ£ÏÂ×î´óµÄÈý¸ö
per(i,0,12)if(cnt[i]>=2){
int res=i;
rd(_,3){
while(vs.back()==i)vs.pop_back();
res=res*15+vs.back();
vs.pop_back();
}return {2,res};
}
//ÂÒÅÅ£ºÕÒ³ö×î´óµÄÎå¸ö
res=find(vs);
return {1,res};
}
string s;
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>s;
while(s!="#"){
mp.clear();
rd(i,2)my[i]=card(s),cin>>s,mp[my[i].hsh()]=1;
rd(i,2)op[i]=card(s),cin>>s,mp[op[i].hsh()]=1;
rd(i,3)co[i]=card(s),cin>>s,mp[co[i].hsh()]=1;
int cnt=0,tot=0;
rd(x,52)if(!mp.count(x))rd(y,52)if(!mp.count(y)&&x!=y){
tot++;
vt<card>A,B;
rd(i,2)A.pb(my[i]),B.pb(op[i]);
rd(i,3)A.pb(co[i]),B.pb(co[i]);
A.pb(card(x)),B.pb(card(x));
A.pb(card(y)),B.pb(card(y));
if(solve(A)>solve(B)){
cnt++;
}
}
// cout<<cnt<<" "<<tot<<endl;
cout<<fixed<<setprecision(10)<<1.0*cnt/tot<<endl;
}
return 0;
}
//Nyn-siyn-hert
D
考虑先用 \(map\) 存下每个给定的数字串可以映射到的词汇串。然后依次处理出数字串的每个区间能否被映射。然后进行 \(dp\),从前往后依次贪心选择最长可行解的进行转移,回溯输出答案。
string ss[10]={"oqz","ij","abc","def","gh","kl","mn","prs","tuv","wxy"};
string dta,word[50005],num[50005];
int n,m,id[26],dp[105],fr[105],f[105][105];
map<string,int>mp;
inline void solve(){
rd(i,10)for(auto j:ss[i])id[j-'a']=i;
cin>>n;m=(int)dta.size();
mp.clear();
rp(i,n){
cin>>word[i];
num[i]="";
for(auto j:word[i])num[i]+=id[j-'a']+'0';
mp[num[i]]=i;
}rep(i,1,m){
string tmp="";
rep(j,i,m){
tmp+=dta[j-1];
if(!mp.count(tmp))f[i][j]=-1;
else f[i][j]=mp[tmp];
}
}dp[0]=1;
rp(i,m)dp[i]=0;
rep(i,0,m-1)if(dp[i]){
rep(j,i+1,m)if(!dp[j]&&f[i+1][j]!=-1){
dp[j]=1,fr[j]=i;
}
}if(!dp[m])cout<<"No solution."<<endl;
else{
vt<int>vans;
int cur=m;
while(cur){
int x=fr[cur];
vans.pb(f[x+1][cur]);
cur=x;
}reverse(vans.begin(),vans.end());
for(auto i:vans)cout<<word[i]<<" ";
cout<<endl;
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>dta;
while(dta!="-1"){
solve();
cin>>dta;
}
return 0;
}
//Nyn-siyn-her
E
考虑转移到前缀异或和,设 \(s_i\) 表示串的前 \(i\) 位异或和,然后发现 \([l,r]\) 异或和为一等价于 \(s_{l-1}=s_r\oplus 1\),为零等价于 \(s_{l-1}=s_r\)。这样就变成了若干个位置对的关系。我们可以先将需要的坐标离散化下来,然后用扩展域并查集查找合法性。
int n,m,fa[20005],b[10005],k,l[5005],r[5005],c[5005];
string s;
inline int head(int x){
return fa[x]==x?x:fa[x]=head(fa[x]);
}
inline void solve(){
cin>>m;k=0;
rp(i,m){
cin>>l[i]>>r[i]>>s;
if(s=="odd")c[i]=1;
else c[i]=0;
b[++k]=l[i]-1,b[++k]=r[i];
}b[++k]=0;sort(b+1,b+1+k);
k=unique(b+1,b+1+k)-b-1;
rp(i,2*k)fa[i]=i;
rp(i,m){
l[i]=lower_bound(b+1,b+1+k,l[i]-1)-b;
r[i]=lower_bound(b+1,b+1+k,r[i])-b;
if(c[i]){
if(head(l[i])==head(r[i])){
cout<<i-1<<endl;
return;
}fa[head(l[i])]=head(r[i]+k);
fa[head(r[i])]=head(l[i]+k);
}else{
if(head(l[i])==head(r[i]+k)){
cout<<i-1<<endl;
return;
}fa[head(l[i])]=head(r[i]);
fa[head(l[i]+k)]=head(r[i]+k);
}
}cout<<m<<endl;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
while(n!=-1){
solve();n=-1;
cin>>n;
}
return 0;
}
//Nyn-siyn-hert
F
考虑枚举最短环中的一个点,并且以这个点为根建立最短路径树。然后我们发现,首先,经过这个点的最短环一定会经过最短路径树上和根节点相邻的一条边。否则找到最短环和最短路径相交的第一个位置,如果不存在,直接使用最短路径替换。如果存在,使用相交前的部分替换,一定不会更劣。
所以,我们枚举它一开始进入了哪一个子树。一旦进入子树中,就不能通过树边返回,必须通过返祖边/横截边。而所有的祖先除了根以外都不能再访问,所以直接查找有没有到根的返祖边,贡献就是最短路径加上返祖边边权。
如果是横截边,发现至少存在一条横跨根的两个子树的横截边,否则不能正常返回。所以枚举这条边的两个端点,只要之间有边并且不在同一个子树中,那么就满足条件。权值是两个点的最短路径长度加上它们之间的边权。
然后就可以解决了,复杂度瓶颈是求 \(n\) 次最短路径树,总复杂度 \(O(nm\log n)\)。
int n,m,a,b,c,fa[105],fr[105],dist[105],e[105][105];
int ex[105],ef[105],ans=1e9;
vt<int>vans;
inline void build(int s){
priority_queue<pii>pq;
rp(i,n)dist[i]=1e9;
dist[s]=0;
pq.push({0,s});
while(pq.size()){
int x=pq.top().second,y=-pq.top().first;
pq.pop();
rp(i,n)if(dist[i]>y+e[x][i]){
dist[i]=y+e[x][i],fa[i]=x;
if(x==s)fr[i]=i;
else fr[i]=fr[x];
pq.push({-dist[i],i});
}
}
}
inline void solve(){
cin>>m;ans=1e9;vans.clear();
rp(i,n)rp(j,n)e[i][j]=1e9;
rp(i,m){
cin>>a>>b>>c;
e[a][b]=min(e[a][b],c);
e[b][a]=min(e[b][a],c);
}rp(x,n){
build(x);
int mn=1e9,px=1,py=0;
rp(i,n)if(i!=x){
ex[i]=1e9,ef[i]=-1;
rp(j,n)if(fr[i]!=fr[j]&&j!=x){
if(e[i][j]+dist[j]<ex[i]){
ex[i]=e[i][j]+dist[j];
ef[i]=j;
}
}if(ex[i]+dist[i]<mn)mn=ex[i]+dist[i],px=i,py=0;
}
rp(i,n)if(fa[i]!=x){
int cost=e[x][i]+dist[i];
if(cost<mn)mn=cost,px=i,py=1;
}
if(mn<ans){
ans=mn;
vans.clear();
if(py){
while(px!=x){
vans.pb(px);
px=fa[px];
}vans.pb(x);
}else{
vt<int>A,B;
int a=px,b=ef[px];
while(a!=x)A.pb(a),a=fa[a];
while(b!=x)B.pb(b),b=fa[b];
reverse(B.begin(),B.end());
for(auto i:A)vans.pb(i);vans.pb(x);
for(auto i:B)vans.pb(i);
}
}
}
if(!vans.size()){
cout<<"No solution."<<endl;
}else {
for(auto i:vans)cout<<i<<" ";
cout<<endl;
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
while(n!=-1){
solve();
cin>>n;
}
return 0;
}
//Nyn-siyn
标签:cnt,rp,int,res,cin,pb,0626
From: https://www.cnblogs.com/jucason-xu/p/17507454.html