ごみの分類
問題の意味
ごみを表す文字列を考えると、26文字の各文字は、特定の組成を表し、ゴミの分類が質問の意味を決定します。
分析
暖かい出席の問題は、ライン上の分裂を書かないように注意してください。
コード
#include <bits/stdc++.h>
using namespace std;
const int N=2050;
int T;
char s[N];
char let[30];
map<char,char> mp;
int main(void){
// freopen("in.txt","r",stdin);
scanf("%d",&T);
for(int cas=1;cas<=T;cas++){
scanf("%s",s);
scanf("%s",let);
mp.clear();
for(int i=0;i<26;i++){
mp['a'+i]=let[i];
}
int d=0,w=0,h=0;
int len=strlen(s);
for(int i=0;i<len;i++){
if(mp[s[i]]=='d'){
d++;
}else if(mp[s[i]]=='w'){
w++;
}else if(mp[s[i]]=='h'){
h++;
}
}
printf("Case #%d: ",cas);
if(4*h>=len){
printf("Harmful\n");
}else if(10*h<=len){
printf("Recyclable\n");
}else{
if(d>=2*w){
printf("Dry\n");
}else{
printf("Wet\n");
}
}
}
return 0;
}
B短縮IPv6アドレス
問題の意味
128ビットのバイナリ数、最初のコロンで区切られた4つの各グループと、32ビットの16進数に変換し、その基は4 1 0 0交換と、その後、0である場合には、所定即ちあなたは、選択することができ、2つのだけのコロンに置き換え連続0の文字列とコロンの文字列は、Qは、辞書順最小で得ることができます。
分析
まず、元の文字列を構築し、その後、8つのグループ、文字列の記録位置し、すべての文字列を検索し、ダブルコロン、直接暴力の後に得られた置換文字列の各セットの先頭を列挙連続0を行い、ソートが可能。
コード
#include <bits/stdc++.h>
using namespace std;
int T;
char s[130];
char hex(char a,char b,char c,char d){
int t=(a-'0')*8+(b-'0')*4+(c-'0')*2+(d-'0');
if(t<=9){
return char(t+'0');
}else{
return char(t-10+'a');
}
}
vector<string> vs;
bool cmp(string a,string b){
int al=a.size();
int bl=b.size();
if(al==bl){
return a<b;
}else{
return al<bl;
}
}
int idx[8];
int main(void){
// freopen("in.txt","r",stdin);
scanf("%d",&T);
for(int cas=1;cas<=T;cas++){
scanf("%s",s);
memset(idx,0,sizeof(idx));
string he="";
int cnt=0;
for(int i=0;i<128;i+=16){
string t="";
bool ac=false;
for(int j=i;j<i+16;j+=4){
char p=hex(s[j],s[j+1],s[j+2],s[j+3]);
if(!ac && p=='0'){
continue;
}
ac=true;
t+=p;
}
int q=he.size();
if(t==""){
he+="0";
}else{
he+=t;
}
idx[cnt++]=q;
if(i==112){
continue;
}
he+=":";
}
vs.clear();
vs.push_back(he);
int hes=he.size();
for(int i=0;i<8;i++){
string tle="";
int t=idx[i];
if(i!=0){
t--;
}
for(int j=0;j<t;j++){
tle+=he[j];
}
int mle=0;
while(t<hes && he[t]=='0' || he[t]==':'){
if(he[t]=='0'){
mle++;
}
t++;
}
if(mle>=2){
tle+="::";
}else{
int j=idx[i];
if(i!=0){
j--;
}
for(;j<t;j++){
tle+=he[j];
}
}
for(int j=t;j<hes;j++){
tle+=he[j];
}
vs.push_back(tle);
}
sort(vs.begin(),vs.end(),cmp);
printf("Case #%d: %s\n",cas,vs[0].c_str());
}
return 0;
}
C回文マウス
問題の意味
文字列が与えられると、リクエストの数\((B)\ ) を満足\(\)と\(B \)は、元の文字列のパリンドロームサブストリングであり、\(\)がされているB(\ \ )ストリング。
分析
全てのパリンドロームサブノードと第パリンドロームツリー構造は、各ノードに対して、答えの寄与は、2つの部分から成り、ポインタ部分は失敗であり、失敗点かかるのAA、AAA、その後、ストリングは、AA、AAAであり、そしてすべてのツリーノード、即ちパリンドロームであるので、他の部分は、次の点およびBBアバとして次のポインタがある場合、ストリングは、アバのBBであり、また、ストリングをABBAべきBB Bのポインタを失敗することが分かります。
だから我々は、各ノードポインタは、各ノードの寄与を計算するには、上下次のポインタジャンプ、乗算定理をジャンプに失敗した回数を見つける必要があります。
例えばBBと同時にDFS暴力ポインタを上に失敗図りながら二重カウントを避けるために、次のDFS Qiuzi同様のツリーサイズの数を求める方法は、各ポインタフラグ暴力を必要としないポインタジャンプBBBBに失敗スキップノードのホップは、DFSの暴力の後もマーカーを空に仕上げました。
コード
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+50;
int vis[N],ndp[N],fdp[N];
struct PT{
int next[N][26],fail[N],cnt[N],num[N],len[N];
int S[N],last,id[N],n,p;
int newnode(int l){
for(int i=0;i<26;i++){
next[p][i]=0;
}
cnt[p]=num[p]=0;
len[p]=l;
return p++;
}
void init(){
p=0;
newnode(0);
newnode(-1);
last=0;
n=0;
S[n]=-1;
fail[0]=1;
}
int getFail(int x){
while(S[n-len[x]-1]!=S[n]){
x=fail[x];
}
return x;
}
void add(int c){
c-='a';
S[++n]=c;
int cur=getFail(last);
if(!next[cur][c]){
int now=newnode(len[cur]+2);
fail[now]=next[getFail(fail[cur])][c];
num[now]=num[fail[now]]+1;
next[cur][c]=now;
}
last=next[cur][c];
cnt[last]++;
id[last]=n;
}
void count(){
for(int i=p-1;i>=0;i--){
cnt[fail[i]]+=cnt[i];
}
}
int dfs(int u){
ndp[u]=1;
fdp[u]=0;
//计算向上跳的fail指针次数,vis保证不重复(比如bb跳的fail指针,bbbb不能再跳),>1保证不走到奇偶根
for(int t=u;!vis[t] && t>1;t=fail[t]){
vis[t]=u;
fdp[u]++;
}
for(int i=0;i<26;i++){
if(next[u][i]){
ndp[u]+=dfs(next[u][i]);
}
}
//清空标记
for(int t=u;vis[t]==u && t>1;t=fail[t]){
vis[t]=0;
}
return ndp[u];
}
ll solve(){
//从两个根dfs
dfs(0);
dfs(1);
ll ans=0;
for(int i=2;i<p;i++){
//除去根,每个节点的贡献(作为另一个回文子串的子串)为
//比如对于样例abba,回文节点bb的next指针指向abba,fail指针指向b
//因此ndp和fdp都为2,贡献为2*2-1=3
//即(b,bb) (b,abba) (bb,bb) (bb,abba),减1就是要减掉本身
ans+=1ll*ndp[i]*fdp[i]-1;
}
return ans;
};
}ac;
int T;
char s[N];
int main(void){
// freopen("in.txt","r",stdin);
scanf("%d",&T);
for(int cas=1;cas<=T;cas++){
scanf("%s",s);
ac.init();
int len=strlen(s);
for(int i=0;i<len;i++){
ac.add(s[i]);
}
ac.count();
memset(vis,0,sizeof(vis));
printf("Case #%d: %lld\n",cas,ac.solve());
}
return 0;
}
D移動
問題の意味
\(N- \)各ボリュームを有するアイテム番目、\(V_I \)を、使用\(K \)ボックスが設置さ、貪欲戦略が大から小にロードされ、機器にインストールすることができ、求めた各ボックスの最小容量。
分析
貪欲な戦略は、箱の容量を作ることは単調ではないので、小さい容量があってもよいが、より大きな容量が最初は小さすぎるだけフィットをもたらす、大きな数をフェッチすることができる場合。
最も単純なアプローチは、容量の下限から直接である\(MAX(v_n、(和 -1)/ K + 1)\) 暴力の列挙を開始し、その後、多重集合を決定します。
- 証拠の上限:
- 容量仮定する\(Vの\) 、フィット感を残りの容量は、各ボックスよりも小さい\(MAXV \)
- 次に\(K×(V-MAXV + 1)<=合計\)
- 精製し\(V <=和/ K + MAXV-1 \)を
以来\(V_I \)の範囲は比較的小さいので、あなたは感情的な瞬間を理解することができ、その後、半分の間で、その後、いくつかの暴力的な地区でも可能であることがわかりました。
コード
#include <bits/stdc++.h>
using namespace std;
const int N=1005;
int T,n,k,a[N];
multiset<int> mt;
bool check(int x){
mt.clear();
for(int i=1;i<=n;i++){
mt.insert(a[i]);
}
for(int i=0;i<k;i++){
int u=0;
while(u<x && !mt.empty()){
auto p=mt.upper_bound(x-u);
if(p==mt.begin()){
break;
}
p--;
u+=*p;
mt.erase(p);
}
}
return mt.empty();
}
int main(void){
// freopen("in.txt","r",stdin);
scanf("%d",&T);
for(int cas=1;cas<=T;cas++){
scanf("%d%d",&n,&k);
int sum=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
sum+=a[i];
}
sort(a+1,a+1+n);
//从下界暴力枚举
int ans;
for(int v=max(a[n],(sum-1)/k+1);;v++){
if(check(v)){
ans=v;
break;
}
}
printf("Case #%d: %d\n",cas,ans);
}
return 0;
}
Gは、今日は金曜日ですか?
問題の意味
nは、日付列、JへのA 9に0を表すが配置され、必要な最小辞書N日が金曜日であるように配置されています。
分析
このため、制限の金曜日、最初の数日、最初の完全な配列の暴力、条件を満たすために、全体の配置を見つける、そして日から全体の配置の残りの部分を判断するためにそう。
計算された日付は、数週間またはツェラーの公式方程式キム・ラーセンかもしれ
コード
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+50;
int T,n;
struct node{
char str[12];
bool operator<(const node& rhs)const{
return string(str)<string(rhs.str);
}
bool operator==(const node& rhs)const{
return string(str)==string(rhs.str);
}
}s[N];
vector<int> mp;
vector<vector<int> > ts;
int ms[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
bool leap(int y){
return ((y%4==0 && y%100!=0) || y%400==0);
}
//判断日期合法性
bool check(int y,int m,int d){
if(m==0 || m>12 || d==0){
return false;
}
int t=ms[m];
if(leap(y) && m==2){
t++;
}
return d<=t;
}
//基姆拉尔森公式
bool cl(int y,int m,int d){
if(!check(y,m,d)){
return false;
}
//题面...
if(y<1600){
return false;
}
//1 2月要转成上一年13 14月
if(m<=2){
m+=12;
y--;
}
int w=((d+2*m+3*(m+1)/5+y+y/4-y/100+y/400+1)%7+7)%7;
return w==5;
}
int main(void){
// freopen("in.txt","r",stdin);
scanf("%d",&T);
for(int cas=1;cas<=T;cas++){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",s[i].str);
}
printf("Case #%d: ",cas);
sort(s+1,s+1+n);
int nn=unique(s+1,s+1+n)-s-1;
//先根据前几个日期筛选出满足条件的全排列(不多),再代入剩下的日期判断
//ts记得清空
ts.clear();
mp.clear();
for(int i=0;i<10;i++){
mp.push_back(i);
}
do{
bool ac=true;
//前4个
for(int i=1;i<=min(nn,4);i++){
int y=mp[s[i].str[0]-'A']*1000+mp[s[i].str[1]-'A']*100+mp[s[i].str[2]-'A']*10+mp[s[i].str[3]-'A'];
int m=mp[s[i].str[5]-'A']*10+mp[s[i].str[6]-'A'];
int d=mp[s[i].str[8]-'A']*10+mp[s[i].str[9]-'A'];
if(!cl(y,m,d)){
ac=false;
break;
}
}
if(ac){
ts.push_back(mp);
}
}while(next_permutation(mp.begin(),mp.end()));
//判断剩下n-4个
int sz=ts.size();
// for(int i=0;i<sz;i++){
// for(int j=0;j<10;j++){
// printf("%d",ts[i][j]);
// }
// printf("\n");
// }
//考虑nn<=4的情况
if(nn<=4){
if(sz){
for(int i=0;i<10;i++){
printf("%d",ts[0][i]);
}
printf("\n");
}else{
printf("Impossible\n");
}
continue;
}
bool ok=false;
for(int i=0;i<sz;i++){
bool ac=true;
for(int j=5;j<=nn;j++){
int y=ts[i][s[j].str[0]-'A']*1000+ts[i][s[j].str[1]-'A']*100+ts[i][s[j].str[2]-'A']*10+ts[i][s[j].str[3]-'A'];
int m=ts[i][s[j].str[5]-'A']*10+ts[i][s[j].str[6]-'A'];
int d=ts[i][s[j].str[8]-'A']*10+ts[i][s[j].str[9]-'A'];
if(!cl(y,m,d)){
ac=false;
break;
}
}
if(ac){
for(int j=0;j<10;j++){
printf("%d",ts[i][j]);
}
printf("\n");
ok=true;
break;
}
}
if(!ok){
printf("Impossible\n");
}
}
return 0;
}
Jのアップグレードテクノロジー
問題の意味
\(\ N-)技術、各スキル有する\(m個\)レベル、\(Iは\)からスキル\(J-1 \)にまで上昇する\(J \)ステージがかかり\(C_ { } IJ \)、\ (N- \)スキルが上昇しているために、\は(私は\)レベルが得られる所得は\(D_I \) 、最大の利益を尋ねます。
分析
質問の暗黙の意味はでき(O(nm)を\)\複雑さを、そうまで上昇すると同時に、それ列挙スキル考慮\(私は\)レベル、そして利点は接頭辞を取得し、その後で最低レベルを列挙することができますちょうど\(私は\)他のスキルはになりますが、その後、このスキルを取るスキルのレベルはまた、接頭辞を得ることができます\(私は\)レベルはちょうど、我々は唯一のプリアウトの各スキルを必要とバラの後ろ最小コストnおよびスキルの最小コストのレベルのアップグレード後。
コード
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1005;
int T,n,m;
ll c[N][N],d[N];
ll dp[N],cp[N][N];
ll mn[N][N],smn[N];
int main(void){
freopen("in.txt","r",stdin);
scanf("%d",&T);
for(int cas=1;cas<=T;cas++){
ll ans=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
cp[i][0]=0;
for(int j=1;j<=m;j++){
scanf("%lld",&c[i][j]);
cp[i][j]=cp[i][j-1]+c[i][j];
}
}
//mn[i][j]表示第i个技能从第j个等级开始的最小前缀和
memset(smn,0,sizeof(smn));
for(int i=1;i<=n;i++){
mn[i][m]=cp[i][m];
ll _mn=mn[i][m];
smn[m]+=cp[i][m];
for(int j=m-1;j>=0;j--){
if(cp[i][j]<_mn){
_mn=cp[i][j];
}
mn[i][j]=_mn;
smn[j]+=mn[i][j];
}
}
dp[0]=0;
for(int i=1;i<=m;i++){
scanf("%lld",&d[i]);
dp[i]=dp[i-1]+d[i];
}
for(int i=0;i<=m;i++){
//枚举相同等级,可以都不升级
for(int j=1;j<=n;j++){
ll tmp=dp[i];
//枚举最低等级的技能
tmp-=cp[j][i];
//剩下的技能找大于等于i级别的最小前缀和之和
tmp-=smn[i];
//最低等级的技能被重复减了
tmp+=mn[j][i];
ans=max(ans,tmp);
}
}
printf("Case #%d: %lld\n",cas,ans);
}
return 0;
}