noip72
T1
考场想法:一眼sb状压dp,然而dp能力过弱,于是直接 \(next\_permutation\) 了,再看了看,发现最后答案不是 \(\max\{a_{i}\}\) ,就是 \(\max\{a_{i}\}+1\) ,算代价的话, \(b\) 的代价是不变的,就是 \(\sum_{i=1}^{n-1}2^{i-1}-1\) ,然而还是不知道如何压,于是40pts跑路了。
正解:
就是状压dp...
40pts: \(next\_permutatoin\) 即可。
80pts:
考虑状压dp,设 \(f_{i,j}\) 表示当前所选集合 \(i\) ,所求出集合能搞出来 \(a\) 的最大值为 \(j\) 的方案数, \(g_{i,j}\) 表示当前状态所有方案的总代价。
转移的话,去枚举当前集合里没有加进来的 \(a_{j}\) ,然后去枚举当前集合能凑出来的最大值 \(k\),是的,直接枚举即可,按照题目说的过程去模拟即可,即如果新加进来的 \(a_{i}==k\) ,那么就由 \(f_{i,k}\) 转移到 \(f_{i|(1<<j-1),k+1}\) ,否则转移到 \(f_{i,\max(a_{j},k)}\) 即可。
这样的话能拿到 \(a_{i}\le n\) 的那档分。
100pts:
发现80pts的状态定义属实冗余,考虑换一个。
发现合并的时候, \(a\) 最多只会+1。
所以对于集合 \(S\) ,就满足 \(\max_{i\in S}\{a_{i}\}\le a\le \max_{i\in S}\{a_{i}\}+|S|-1\)
所以只需要记录 \(a\) 相对于 \(\max_{i\in S}\{a_{i}\}\) 增加了多少即可。
然而事实上,第二维只用0/1即可。
Code
#include<cstdio>
#include<cctype>
#include<algorithm>
#define re register
#define MAX 4000003
#define int long long
#define scanf oma=scanf
using std::next_permutation;
const int N = 20;
const int mod = 1e9+7;
int oma;
namespace some
{
struct stream
{
template<typename type>inline stream &operator >>(type &s)
{
bool w=0; s=0; char ch=getchar();
while(!isdigit(ch)){ w|=ch=='-'; ch=getchar(); }
while(isdigit(ch)){ s=(s<<1)+(s<<3)+(ch^48); ch=getchar(); }
return s=w?-s:s,*this;
}
}cin;
int n,mul,a[N];
//int biao[MAX][20],cnt,id[MAX],tot;
#define debug(s) debug((char*)s)
auto debug = [](char* s) { printf("%s\n",s); };
auto max = [](int a,int b) -> int { return a>b?a:b; };
}using namespace some;
namespace simple
{
int p[N];
int xam,cost;
auto solve = []() -> void
{
for(re int i=1; i<=n; i++)
{ p[i] = i; }
int pa,pb,pc;
do
{
pa = a[p[1]],pb = pc = 0/*,++cnt*/;
for(re int i=2; i<=n; i++)
{
if(pa!=a[p[i]])
{ pa = max(pa,a[p[i]]); }
else
{ pa++; }
pc += (mul*pa%mod+pb)%mod,pb = 2*pb+1;
}
//for(re int i=1; i<=n; i++) { biao[cnt][i] = p[i]; /*printf("%d ",p[i]);*/ }
//printf("\n%d %d\n",pa,xam);
if(pa>xam)
{
xam = pa,cost = pc;
//id[tot = 1] = cnt;
}
else if(pa==xam)
{ /*id[++tot] = cnt,*/cost = (cost+pc)%mod; }
}while(next_permutation(p+1,p+1+n));
//printf("%d\n",xam);
/*for(re int i=1; i<=tot; i++)
{
for(re int j=1; j<=n; j++)
{ printf("%d ",biao[id[i]][j]); }
printf("\n");
}*/
printf("%lld %lld\n",xam,cost);
};
};
namespace subtask
{
int xam,add;
int f[1<<N][N],g[1<<N][N];
auto task = []() -> void
{
xam = a[n];
for(re int i=1; i<=n-1; i++)
{ xam = max(a[i],xam),(add += (1<<i-1)-1) %= mod; }
//printf("%lld\n",add);
int sta = (1<<n)-1;
for(re int i=1; i<=n; i++)
{ f[1<<(i-1)][a[i]] = 1; }
for(re int i=1; i<sta; i++)
{
for(re int j=1; j<=n; j++)
{
if(!((i>>j-1)&1))
{
for(re int k=1; k<=xam+1; k++)
{
if(k==a[j])
{
(f[i|(1<<j-1)][k+1] += f[i][k]) %= mod;
(g[i|(1<<j-1)][k+1] += (g[i][k]+mul*(k+1)%mod*f[i][k]%mod)%mod) %= mod;
}
else
{
(f[i|(1<<j-1)][max(a[j],k)] += f[i][k]) %= mod;
(g[i|(1<<j-1)][max(a[j],k)] += (g[i][k]+mul*max(a[j],k)%mod*f[i][k]%mod)%mod) %= mod;
}
}
}
}
}
if(f[sta][xam+1])
{
printf("1: cnt=%lld mul=%lld\n",f[sta][xam+1],g[sta][xam+1]);
printf("%lld %lld\n",xam+1,((g[sta][xam+1]+add*f[sta][xam+1]%mod)%mod)%mod);
}
else
{
printf("2: cnt=%lld mul=%lld\n",f[sta][xam],g[sta][xam]);
printf("%lld %lld\n",xam,((g[sta][xam]+add*f[sta][xam]%mod)%mod)%mod);
}
};
};
namespace right
{
int ans,add,xam[1<<N];
int f[1<<N][2],g[1<<N][2];
auto solve = []() -> void
{
ans = a[n];
for(re int i=1; i<n; i++)
{ ans = max(ans,a[i]); (add += (1<<i-1)-1) %= mod; }
int sta = (1<<n)-1;
for(re int i=1; i<=n; i++)
{ f[1<<(i-1)][0] = 1; }
for(re int i=1; i<=sta; i++)
{
for(re int j=1; j<=n; j++)
{ if((i>>j-1)&1){ xam[i] = max(xam[i],a[j]); } }
}
for(re int i=1,w; i<sta; i++)
{
//printf("sta=%lld xam=%lld\n",i,xam);
for(re int k=0; k<=1; k++)
{
if(!f[i][k])
{ continue ; }
for(re int j=1; j<=n; j++)
{
if(!((i>>j-1)&1))
{
w = k+xam[i]==a[j]?a[j]+1:max(a[j],k+xam[i]);
(f[i|(1<<j-1)][w-xam[i|(1<<j-1)]] += f[i][k]) %= mod;
(g[i|(1<<j-1)][w-xam[i|(1<<j-1)]] += (g[i][k]+mul*w%mod*f[i][k]%mod)%mod) %= mod;
}
}
}
}
if(f[sta][1])
{
//printf("1: cnt=%lld mul=%lld\n",f[sta][1],g[sta][1]);
printf("%lld %lld\n",ans+1,(g[sta][1]+add*f[sta][1]%mod)%mod);
}
else
{
//printf("2: cnt=%lld mul=%lld\n",f[sta][0],g[sta][0]);
printf("%lld %lld\n",ans,(g[sta][0]+add*f[sta][0]%mod)%mod);
}
};
};
namespace OMA
{
auto main = []() -> signed
{
//freopen("my.out","w",stdout);
freopen("repair.in","r",stdin); freopen("repair.out","w",stdout);
cin >> n >> mul;
for(re int i=1; i<=n; i++)
{ cin >> a[i]; }
//if(n<=10) { simple::solve(); }
//else { subtask::task(); }
right::solve();
return 0;
};
}
signed main()
{ return OMA::main(); }
T2
考场想法: 参考noip71,这题目名应该没有骗我,于是开场先写这道,发现只会 \(O(nq\log v)\) 于是死了,准备40pts跑路,发现部分分可做,于是又去写了 \(w,h\le 2000\) 的点,然后想写光速幂,不会,试图自己造出光速幂,想到根号处理后,发现不会了,于是爬了。
有59pts,考后再交了一遍,就有71pts,再交就47pts....
正解:
T3
考场想法:上来先冲二分,很快就冲出来了,然而假了,大样例跑的飞快,就是不对。于是放弃二分,短暂沉思之后,糊了个hash暴力匹配,再次短暂的沉思,想了kmp,然而忘记了,又想了想,kmp每次处理是 \(O(len)\) ,不如我直接hash暴力匹配+玄学剪枝。再次沉思,想写个ac自动机,然而不知道如何用ac自动机写这道题,于是再写了俩子任务跑路了。
因为数据过氵,所以拿了95pts。
那5pts是因为 \(n=10\) 的点,开的记录数组初始值是0,这样如果两个串匹配答案是0的话,下一次还会计算,所以死了,赛后 \(memset\) 成-1就过了。
hash暴力匹配+玄学剪枝就可以草过去。
当然你也可以选择分块草过去
正解:
就是ac自动机,但不会。
所以粘一下
T4
考场想法:不会,只有搜。
10pts。
赛后输出1有45pts....
正解: