A - こだわり者いろはちゃん / Iroha's Obsession(暴力)
効果の件名:
もし\(K \)すべての数字とデジタル\(N \) 、より数の大きいを発見またはNに等しく、そのようなことは必要\(K \)数。
一般的な考え方:
もう一つの最大の列挙、ラインで直接列挙します。
コード:
#include<bits/stdc++.h>
using namespace std;
int n,k;
int vis[20];
bool check(int x){
while(x){
int d=x%10;
x/=10;
if(vis[d])return 0;
}
return 1;
}
int main()
{
//freopen("H:\\c++1\\in.txt","r",stdin);
//freopen("H:\\c++1\\out.txt","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1,x;i<=k;i++)scanf("%d",&x),vis[x]=1;
for(int i=n;;i++){
if(check(i)){
printf("%d\n",i);return 0;
}
}
return 0;
}
B - いろはちゃんとマス目 / Iroha and a Grid(组合数)
効果への質問の意味:
A \(H \)と\(W \)長方形マトリックス、その左下隅に\(X \)と\(Y \)の矩形行列が下ると右側の右下隅に左上から求めることはできませんプログラム番号\(()(H、W <= 1E5)\)
一般的な考え方:
あなたが知っているすべてのまず、何のグリッドが存在しない場合から行くことができません\((X1、Y1)\ ) 行ってきました(\(X2、Y2)\ ) プログラムの数があるべき\(C(X2 + Y2- X1-Y1、X2 -X1)\) 、我々は、左上位置の座標と仮定\ \((1,1)) 、右下隅の位置座標\((H、W)\)、その後に)(X = X \ \分割線として、実行可能な部分は、各プログラムは、それが想定され、その後、右の境界を通過しなければならないため、異なるサイズの2つの矩形に分割して\(Y = I \) 、私たちに必要な\((X、I)\ 1 X +(に、I)\) 、我々はちょうどから算出した((1,1)\ \(と X、I)\) プログラムの数\(* \)から\((X + 1、I )\(H、Wへ)\)プログラム番号の、そして境界線上の各点を加算することは答えることができ、かつ事前階乗階乗逆要素。
コード:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+10;
const int mod=1e9+7;
ll n,m,a,b;
ll ksm(ll a,ll b){
ll res=1,t=a;
while(b){
if(b&1)res=(res*t)%mod;
t=(t*t)%mod;
b>>=1;
}
return res;
}
ll jc[N],inv[N];
void init(){
jc[0]=inv[0]=1;
for(int i=1;i<N;i++)jc[i]=(jc[i-1]*i)%mod,inv[i]=ksm(jc[i],mod-2);
}
ll C(ll a,ll b){
return ((jc[b]*inv[a])%mod*inv[b-a])%mod;
}
int main()
{
//freopen("H:\\c++1\\in.txt","r",stdin);
//freopen("H:\\c++1\\out.txt","w",stdout);
init(); // 预处理阶乘和逆元
scanf("%lld%lld%lld%lld",&n,&m,&a,&b);
ll ans=0;
for(int i=1;i<=n-a;i++){
ll ans1=C(b-1,b+i-2); //分界线两个对应的点
ll ans2=C(m-b-1,n-i+m-b-1);
ans=(ans+ans1*ans2)%mod;
}
printf("%lld\n",ans);
return 0;
}
C - 和風いろはちゃん / Iroha and Haiku(状压DP)
効果の件名:
所与\(,, X、Y、Z \) 、要求発生\(N- \)番号の配列を、それぞれ数である\(1-10 \)の条件を満たすことが可能な全ての配列を求め、:が存在\ (1 <= X <Y <Z <W <= N \)、そう\(\和^ {X} _ {Y-1} {a_iを} = X、\和^ {Y} _ {Z-1} {a_iを} = Y、\和 ^ {Z} _ {W-1} {a_iを} = Z \) 数列です。\((X <= 5、 Y <= 7、Z <= 5、N <= 40)\)
一般的な考え方:
最初は、そのようなデータは成形圧力DPを解決するために考慮すべきトピックの小範囲について、最初のデジタルため\(5 \) 、我々はバイナリ使用できる\(10000 \)を三つの連続セグメントが形成されされるのを示すために\(2,3,1 ,, \)状態がバイナリであってもよい(\ 101001)\一緒にスプライスして、次に配列について前記し\(,,, 2,1,2,1 \)のバイナリ状態とすることができる(101101 \)\、上記に見出すことができる\(101001 \ 101001&101101 = \)を表す(,,, 2,1,2,1 \)\配列満たす三つの連続セグメントそして、のために\(2,3,1 ,, \)です。私たちは、私たちが追求すべき状態のこのような構造を介して転送することができ、最後の減算で条件を満たしていない数列です。
コード:
#include<bits/stdc++.h>
using namespace std;
const int N=(1<<18);
const int mod=1e9+7;
int dp[50][N];
int n,a,b,c;
int main()
{
//freopen("H:\\c++1\\in.txt","r",stdin);
//freopen("H:\\c++1\\out.txt","w",stdout);
scanf("%d",&n);
scanf("%d",&a);scanf("%d",&b);scanf("%d",&c);
int bz=(1<<(a-1))|(1<<(a+b-1))|(1<<(a+b+c-1));//目标状态
int mx=(1<<(a+b+c));//总共的状态数
dp[0][0]=1;//初始化
int p=1;
for(int i=1;i<=n;i++){
p=(1ll*10*p)%mod;//计算总共的方案数
for(int j=0;j<mx;j++){//枚举所有状态
if(dp[i-1][j]==0)continue;//上一个此状态无解
for(int k=1;k<=10;k++){//枚举每种方案
int now=(j<<k)|(1<<(k-1));//更新状态
now&=(mx-1);//防止溢出
if((now&bz)!=bz){//不能构成X,Y,Z
dp[i][now]=(dp[i][now]+dp[i-1][j])%mod;
}
}
}
}
int ans=p;
for(int i=0;i<mx;i++){
ans=(ans-dp[n][i]+mod)%mod;//
}
printf("%d\n",ans);
return 0;
}
D - 文字列大好きいろはちゃん / Iroha Loves Strings(暴力+dp)
効果の件名:
所与\(N \)文字列、一部の文字列は、それらの全体の長さとなるように選択\(K \) 、及びそれらが保証辞書最小解けるために一緒に接合されます。\((N <= 2000、 K <= 1E4、len_i <= K \和{len_i} <= 1E6)\)
一般的な考え方:
まず、に予め結合した\(DP [I] [J] \) 、表示(\で)\文字列を構成することができる(\ J)\文字列の長さ用いて\(ビットセット\)最適化バックパック01
私たちが使用できるようになり、最初の文字列\(1 \)を最初のビットは、すべての答えを構築し、キューに入れられた\(I \)ビットは、キュー内のすべての文字の最小値、次の更新でなければなりませんキューは、文字列の最後の現在の位置ならば、それはまだ文字列を追加した後、それの終わりに達していない場合、我々は、文字列の後ろにキューに参加する最初のことができることを意味します。
(これは実際には正の解でなく、ラインの複雑さ、正のソリューションを使用していないです\(exkmp \) 、調査研究)
コード:
//这题很玄,可能会re或者wa
#include<bits/stdc++.h>
using namespace std;
bitset<10010> ok[2010];
pair<int,int> p[2][10010];
char s[2010][10010],ans[1000010];
int len[2010],n,k,cnt=0,ncnt=0;
int main()
{
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++) scanf("%s",s[i]+1),len[i]=strlen(s[i]+1);
ok[n+1][0]=1;
for (int i=n;i>=1;i--)
ok[i]=ok[i+1]|(ok[i+1]<<len[i]);//01背包处理可以拼成的长度
for (int i=1;i<=n;i++)
if (ok[i+1][k-len[i]]) p[0][++cnt]=make_pair(i,1);//加入,使用滚动数组
int f=0;
for (int i=1;i<=k;i++)
{
char mi='z';
for (int j=1;j<=cnt;j++) mi=min(mi,s[p[f][j].first][p[f][j].second]);//贪心选择最小字符
ans[i]=mi;
int mx=n+1;//初始化为大值
ncnt=0;
for (int j=1;j<=cnt;j++)
{
int id=p[f][j].first,pos=p[f][j].second;
if (s[id][pos]!=mi) continue;
if (pos==len[id]) mx=min(mx,id);//当前字符串结束,可以选择当前字符串后面的所以字符串
else p[f^1][++ncnt]=make_pair(id,pos+1);//继续使用这个字符串
}
for (int j=mx+1;j<=n;j++)
if (k-len[j]-i>=0&&ok[j+1][k-len[j]-i]) p[f^1][++ncnt]=make_pair(j,1);//判断是否可以用该字符串
f^=1;//
cnt=ncnt;//
}
printf("%s\n",ans+1);
}