这场比赛好难啊。。。
最终成绩:
如果不算 Hash_Table
自己切掉的 J 题,自己还能在区域赛搞个 Ag?
题意:求有多少小于 2N 且形如 2k−1 且能被 7 整除的数。
题解:签到题。printf("%d\n, n / 3);
Hash_Table’s Code:
#include
using namespace std;
int n,T;
int main()
{
cin>>T;
for(int i=1;i<=T;i++)
{
cin>>n;
printf("Case #%d: %d\n",i,n/3);
}
return 0;
}
题意:定义「半回文串」是奇数位或偶数位构成回文串的字符串,如 101111
,111
,11101
是「半回文串」 ,而 101001
,11000
不是。
求长度为 N 字典序第 K 小的字符集为 0,1
的「半回文串」 。
N≤105,K≤1018
题解: 按位考虑,枚举当前位填什么,然后算出以当前位为前缀的「半回文串」的数目,确定当前位的数字。需要考虑很多种情况,代码很恶心。
My Code:
#include
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
int n; ll m;
int N;
int a[100005];
int flag = 0;
ll pw[100005];
void dfs(int cur, ll k, int f0, int f1){
//printf("%d %d %d %d\n", cur, k, f0, f1);
if(cur > n) return;
ll tmp = 1;
int cr = cur;
if(cur <= N){
tmp = tmp * pw[N - cur];
if(tmp >= k){
a[cur] = 0;
dfs(cur + 1, k, 1, 1);
return;
}
cr = n - N;
// printf("%d %d\n", cur, N);
ll t1 = pw[cr / 2] + pw[cr - cr / 2] - 1;
if(t1 >= k / tmp + 1){
a[cur] = 0;
dfs(cur + 1, k, 1, 1);
return;
}
tmp *= t1;
if(tmp >= k){
a[cur] = 0;
dfs(cur + 1, k, 1, 1);
return;
}
k -= tmp;
a[cur] = 1;
dfs(cur + 1, k, 1, 1);
return;
}
if(f0 == 1 && f1 == 1){
ll t0 = 0, t1 = 0;
int cr;
if(cur % 2 == 0 && n % 2 == 0) cr = n - cur + 2;
if(cur % 2 == 0 && n % 2 == 1) cr = n - cur + 1;
if(cur % 2 == 1 && n % 2 == 0) cr = n - cur;
if(cur % 2 == 1 && n % 2 == 1) cr = n - cur + 1;
int ccr = n - cur + 1;
if(cr % 2 == 0){
if(ccr % 2 == 0){
if(a[cr] == 0) t0 += pw[ccr / 2] + pw[ccr / 2 - 1] - 1, t1 += pw[ccr / 2 - 1];
else t1 += pw[ccr / 2] + pw[ccr / 2 - 1] - 1, t0 += pw[ccr / 2 - 1];
}else{
if(a[cr] == 0) t0 += pw[ccr / 2] + pw[ccr / 2] - 1, t1 += pw[ccr / 2];
else t1 += pw[ccr / 2] + pw[ccr / 2] - 1, t0 += pw[ccr / 2];
}
}else{
if(ccr % 2 == 0){
if(a[cr] == 0) t0 += pw[ccr / 2] + pw[ccr / 2 - 1] - 1, t1 += pw[ccr / 2 - 1];
else t1 += pw[ccr / 2] + pw[ccr / 2 - 1] - 1, t0 += pw[ccr / 2 - 1];
}else{
if(a[cr] == 0) t0 += pw[ccr / 2] + pw[ccr / 2] - 1, t1 += pw[ccr / 2];
else t1 += pw[ccr / 2] + pw[ccr / 2] - 1, t0 += pw[ccr / 2];
}
}
if(t0 >= k){
a[cur] = 0;
if(a[cr] == 0) dfs(cur + 1, k, 1, 1);
else if(cr % 2 == 0) dfs(cur + 1, k, 0, 1);
else if(cr % 2 == 1) dfs(cur + 1, k, 1, 0);
return;
}
k -= t0;
if(t1 >= k){
a[cur] = 1;
if(a[cr] == 1) dfs(cur + 1, k, 1, 1);
else if(cr % 2 == 0) dfs(cur + 1, k, 0, 1);
else if(cr % 2 == 1) dfs(cur + 1, k, 1, 0);
return;
return;
}
flag = 1;
return;
}
if(f0 == 0 && f1 == 1){
ll t0 = 0, t1 = 0;
int cr;
if(cur % 2 == 0 && n % 2 == 0) cr = n - cur + 2;
if(cur % 2 == 0 && n % 2 == 1) cr = n - cur + 1;
if(cur % 2 == 1 && n % 2 == 0) cr = n - cur;
if(cur % 2 == 1 && n % 2 == 1) cr = n - cur + 1;
int ccr = n - cur + 1;
if(cr % 2 == 0){
if(ccr % 2 == 0){
t0 += pw[ccr / 2 - 1], t1 += pw[ccr / 2 - 1];
}else{
t0 += pw[ccr / 2], t1 += pw[ccr / 2];
}
}else{
if(ccr % 2 == 0){
if(a[cr] == 0) t0 += pw[ccr / 2];
else t1 += pw[ccr / 2];
}else{
if(a[cr] == 0) t0 += pw[ccr / 2];
else t1 += pw[ccr / 2];
}
}
if(t0 >= k){
a[cur] = 0;
dfs(cur + 1, k, 0, 1);
return;
}
k -= t0;
if(t1 >= k){
a[cur] = 1;
dfs(cur + 1, k, 0, 1);
return;
}
flag = 1;
return;
}
if(f0 == 1 && f1 == 0){
ll t0 = 0, t1 = 0;
int cr;
if(cur % 2 == 0 && n % 2 == 0) cr = n - cur + 2;
if(cur % 2 == 0 && n % 2 == 1) cr = n - cur + 1;
if(cur % 2 == 1 && n % 2 == 0) cr = n - cur;
if(cur % 2 == 1 && n % 2 == 1) cr = n - cur + 1;
int ccr = n - cur + 1;
if(cr % 2 == 1){
if(ccr % 2 == 0){
t0 += pw[ccr / 2 - 1], t1 += pw[ccr / 2 - 1];
}else{
t0 += pw[ccr / 2], t1 += pw[ccr / 2];
}
}else{
if(ccr % 2 == 0){
if(a[cr] == 0) t0 += pw[ccr / 2];
else t1 += pw[ccr / 2];
}else{
if(a[cr] == 0) t0 += pw[ccr / 2];
else t1 += pw[ccr / 2];
}
}
if(t0 >= k){
a[cur] = 0;
dfs(cur + 1, k, 1, 0);
return;
}
k -= t0;
if(t1 >= k){
a[cur] = 1;
dfs(cur + 1, k, 1, 0);
return;
}
flag = 1;
return;
}
}
void Solve(){
N = ((n - 1) / 4 + 1) * 2 - (n % 4 == 1);
flag = 0;
dfs(1, m, 1, 1);
if(flag == 1 || (n == 1 && m > 2) || (n == 2 && m > 4)){
printf("NOT FOUND!\n");
return;
}
for(int i = 1; i <= n; i ++){
printf("%d", a[i]);
}
printf("\n");
}
int main(){
int T;
pw[0] = 1;
for(int i = 1; i <= 100000; i ++){
pw[i] = pw[i - 1] * 2;
if(pw[i] > 1e18) pw[i] = 1e18 + 2;
}
scanf("%d", &T);
for(int I = 1; I <= T; I ++){
scanf("%d%I64d", &n, &m);
printf("Case #%d: ", I);
Solve();
}
return 0;
}
题意:一个长度为 N 的序列,要求选出两段不重叠的区间,要求两个区间包含的元素互不相同,最大化两段区间的长度和。
N≤1000
题解:枚举右区间左端点,然后不断右移右端点,用 std::set
维护左面的情况。 复杂度 O(N2log2N) 。
Hash_Table’s Code:
#include
#define debug(x) cout<<#x<<"="<
using namespace std;
const int maxn = 1009;
int t[maxn],a[maxn],cnt[maxn];
bool vis[maxn];
int n,T,tot,ans,poi;
pair<int,int> b[maxn];
vector<int> pos[maxn];
setint ,int> > S;
setint ,int> >::iterator it,tmp;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int getans()
{
while(!cnt[poi]&&poi) poi--;
if(poi==0) return 0;
return poi;
}
void work(int x)
{
it=S.lower_bound(make_pair(x+1,0));
if(it==S.begin()) return ;
it--;
int L=x,R=x;
while(true)
{
if((*it).secondbreak;
int l=(*it).first,r=(*it).second;
cnt[r-l+1]--;
L=min(L,l);R=max(R,r);
if(it==S.begin()){S.erase(it);break;}
tmp=it;it--;S.erase(tmp);
}
if(Lint,int> line=make_pair(L,x-1);
if(S.find(line)==S.end()) S.insert(line),cnt[x-L]++;
}
if(xint,int> line=make_pair(x+1,R);
if(S.find(line)==S.end()) S.insert(line),cnt[R-x]++;
}
}
void ClearLove()
{
for(int i=1;i<=tot;i++) pos[i].clear();
ans=tot=0;
}
int main()
{
T=read();
for(int I=1;I<=T;I++)
{
n=read();ClearLove();
for(int i=1;i<=n;i++) a[i]=t[i]=read();
sort(t+1,t+1+n);tot=unique(t+1,t+1+n)-t-1;
for(int i=1;i<=n;i++) a[i]=lower_bound(t+1,t+1+tot,a[i])-t,pos[a[i]].push_back(i);
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof vis);
int now=i;vis[a[now]]=1;
while(!vis[a[now+1]]&&now1;
b[i]=make_pair(i,now);
}
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof vis);
memset(cnt,0,sizeof cnt);
S.clear();poi=n;
for(int j=i+1;j<=n;j++) S.insert(b[j]),cnt[b[j].second-b[j].first+1]++;
for(int j=i;j>=1;j--)
{
int x=a[j],sz=pos[x].size();
// debug(x);
if(vis[x]) break;vis[x]=1;
for(int k=0;kint p=pos[x][k];
work(p);
}
ans=max(ans,i-j+1+getans());
}
}
printf("Case #%d: %d\n",I,ans);
}
return 0;
}
题意:有 N 个冰激凌,你需要做出一些大小均为 K 的冰激凌塔。
一个冰激凌塔 {a1,a2,…,ak} 需要满足对于任意 i<k ,都满足 2ai≤ai+1 。
问最多能做出多少个冰激凌塔。
N≤3×105 。
题解:二分答案,贪心的从小到大选择,不断填满每一层。因为如果当前冰激凌不能放在第 i 层,显然不能放在第 i+1 层,贪心的正确性得以证明。
My Code:
#include
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
int n, k;
ll a[300005];
ll b[300005][2]; int tp[300005];
int judge(int mid){
for(int i = 1; i <= mid; i ++){
b[i][1] = a[i];
}
int p = 0, q = 1;
int pos = mid + 1;
for(int i = 2; i <= k; i ++){
for(int j = 1; j <= mid; j ++){
while(pos <= n && a[pos] < b[j][q] * 2) pos ++;
if(pos > n) return 0;
b[j][p] = a[pos];
pos ++;
}
swap(p, q);
}
return 1;
}
int main(){
int T;
scanf("%d", &T);
for(int I = 1; I <= T; I ++){
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i ++){
scanf("%I64d", &a[i]);
}
sort(a + 1, a + n + 1);
int l = 0, r = n / k;
while(l <= r){
int mid = (l + r) >> 1;
if(judge(mid)) l = mid + 1;
else r = mid - 1;
}
printf("Case #%d: %d\n", I, r);
}
return 0;
}
题意:你参加了一次赌球。赌球的规则如下:
一共有 N 支队伍参赛,第 i 支队伍有一个形如 AiBi 的赔率。你可以给每支队伍下注(任意金额)。加入你给一支队伍下注 x 元,如果该队没有胜利则输掉本金,如果该队获胜则返还本金,并另外获得 xBiAi 的奖金。你需要求出最多给多少个队伍下注(给每个队伍下注大于 0 的可以不相同的任意金额),使得任意一个你下注的队伍获胜,你都可以赚钱。
N≤100
题解:考虑你下注的每一支队伍,都需要满足:(设 pi 为给第 i 支队伍下的赌注占总赌注的比例)
long double
。 复杂度
O(Nlog2N) 。
My Code:
#include
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
const ll mod = 1e9 + 7;
long double d[100005];
int n, m;
int main(){
int T;
scanf("%d", &T);
for(int I = 1; I <= T; I ++){
scanf("%d", &n);
for(int i = 1; i <= n; i ++){
double x, y;
scanf("%lf:%lf", &x, &y);
int xx = (int)(x * 1000 + 0.01);
int yy = (int)(y * 1000 + 0.01);
d[i] = 1.0 * xx / (long double)(xx + yy);
}
sort(d + 1, d + n + 1);
long double sum = 0;
int cnt = 0;
for(int i = 1; i <= n; i ++){
sum = sum + d[i];
if(sum >= 1) break;
cnt++;
}
printf("Case #%d: %d\n", I, cnt);
}
return 0;
}
题意:有 n 个小写字母构成的字符串 s1,s2,…sn ,定义「魔咒」为仅为 s1 的子串,且长度最小(长度相同的取字典序最小)的字符串。求出「魔咒」或者回答其不存在。
n≤5×104,∑ni=1|si|≤3×106 。
题解,把 n 个字符串用分隔符隔开建一个后缀数组。对于每一个属于 s1 的后缀,求出与他的 lcp 最长的不属于 s1 的后缀 (可以利用 height 数组的单调性用指针维护),然后答案是 s1 与其他后缀 lcp 的最大的位置开始的长度为 lcp+1 的子串。
My Code:
#include
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
int sa[300005], wys[300005], wv[300005], rk[300005], rk2[300005], h[300005];
int cal(int *r, int i, int j, int l){
return r[i] == r[j] && r[i + l] == r[j + l];
}
void DA(int *r, int *sa, int n, int m){
int *x = rk, *y = rk2, *t;
for(int i = 1; i <= m; i ++) wys[i] = 0;
for(int i = 1; i <= n; i ++) wys[x[i] = r[i]] ++;
for(int i = 1; i <= m; i ++) wys[i] += wys[i - 1];
for(int i = n; i >= 1; i --) sa[wys[x[i]] --] = i;
for(int j = 1, p = 1; j <= n; j <<= 1, m = p){
p = 0;
for(int i = n - j + 1; i <= n; i ++) y[++p] = i;
for(int i = 1; i <= n; i ++) if(sa[i] - j > 0) y[++p] = sa[i] - j;
for(int i = 1; i <= n; i ++) wv[i] = x[y[i]];
for(int i = 1; i <= m; i ++) wys[i] = 0;
for(int i = 1; i <= n; i ++) wys[wv[i]] ++;
for(int i = 1; i <= m; i ++) wys[i] += wys[i - 1];
for(int i = n; i >= 1; i --) sa[wys[wv[i]] --] = y[i];
t = x; x = y; y = t; p = 2; x[sa[1]] = 1;
for(int i = 2; i <= n; i ++){
x[sa[i]] = cal(y, sa[i], sa[i - 1], j) ? p - 1 : p++;
}
}
}
void calheight(int *r, int *sa, int n, int m){
int k = 0;
for(int i = 1; i <= n; i ++) rk[sa[i]] = i;
for(int i = 1; i <= n; i ++){
if(rk[i] == 1) {h[rk[i]] = 0; continue;}
int j = sa[rk[i] - 1];
while(r[i + k] == r[j + k]) k ++;
h[rk[i]] = k;
if(k > 0) k--;
}
}
int a[300005][20];
int lg[300005];
int N;
void build_rmq(){
int t1 = -1, t2 = 1;
for(int i = 1; i <= N; i ++){
if(t2 <= i) t2 <<= 1, t1 ++;
lg[i] = t1;
}
for(int i = 1; i <= N; i ++){
a[i][0] = h[i];
}
for(int j = 1; j <= lg[N]; j ++){
for(int i = 1; i + (1 << j) - 1 <= N; i ++){
a[i][j] = min(a[i][j - 1], a[i + (1 << (j - 1))][j - 1]);
}
}
}
int lcp(int l, int r){
if(l == r) return N - sa[l] + 1;
l++;
int ln = r - l + 1;
//printf("%d %d %d %d\n", l, r, lg[ln], r - (1 << lg[ln]) + 1);;
return min(a[l][lg[ln]], a[r - (1 << lg[ln]) + 1][lg[ln]]);
}
int n;
int rs[300005];
char ch[300005];
int bl[300005], len[300005];
int pre[300005], nxt[300005];
void Solve(){
scanf("%d", &n);
N = 1;
rs[1] = 128;
for(int i = 1; i <= n; i ++){
scanf("%s", ch + 1);
len[i] = strlen(ch + 1);
for(int j = 1; j <= len[i]; j ++){
rs[N + j] = ch[j];
bl[N + j] = i;
}
N += len[i];
N ++;
rs[N] = 128 + i;
}
DA(rs, sa, N, 128 + n);
calheight(rs, sa, N, 128 + n);
build_rmq();
int pos = 0;
for(int i = N; i >= 1; i --){
nxt[i] = pos;
if(bl[sa[i]] != 1){
pos = i;
}
}
pos = 0;
for(int i = 1; i <= N; i ++){
pre[i] = pos;
if(bl[sa[i]] != 1){
pos = i;
}
}
int ans = 0x3f3f3f3f; pos = 0;
for(int i = 2; i <= len[1] + 1; i ++){
int tmp = 0;
if(pre[rk[i]]){
tmp = max(tmp, lcp(pre[rk[i]], rk[i]));
}
if(nxt[rk[i]]){
tmp = max(tmp, lcp(rk[i], nxt[rk[i]]));
}
tmp = tmp + 1;
//printf("%d %d %d %d %d\n", rk[i], pre[rk[i]], nxt[rk[i]], lcp(rk[i], pre[rk[i]]), lcp(rk[i], nxt[rk[i]]));
if(tmp > len[1] - i + 2) continue;
if(tmp < ans || (tmp == ans && rk[i] < rk[pos])){
ans = tmp;
pos = i;
}
}
if(ans > len[1]){
printf("Impossible\n");
}else{
for(int i = 1; i <= ans; i ++){
putchar(rs[pos + i - 1]);
}
putchar('\n');
}
}
int main(){
int T;
scanf("%d", &T);
for(int I = 1; I <= T; I ++){
printf("Case #%d: ", I);
Solve();
}
return 0;
}
坑。
题意:有一个 N 行 M 列的矩阵,每一个格子是 [1,k] 之间的一个整数。
定义一个格子是吼的,当且仅当该格子的值严格大于与其同行同列的所有格子种的值。定义 Ag 为恰好有 g 个吼的格子的互不相同的矩阵数目,求:
题解:
(总感觉这道题那么眼熟,好像是之前做过的某场NOIP模拟赛?)
先把式子拆一下:
最后的答案为:
My Code:
#include
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
const ll mod = 1e9 + 7;
ll qpow(ll a, ll b){
ll ret = 1;
for(; b; b >>= 1, a = a * a % mod){
if(b & 1) ret = ret * a % mod;
}
return ret;
}
int n, m, k;
int main(){
int T;
scanf("%d", &T);
for(int I = 1; I <= T; I ++){
scanf("%d%d%d", &n, &m, &k);
ll ans = 0;
for(int i = 1; i <= k; i ++){
ans = (ans + qpow(i - 1, n - 1 + m - 1) * qpow(k, (n - 1) * (m - 1)) % mod * n % mod * m % mod) % mod;
}
ans = (ans + qpow(k, n * m)) % mod;
printf("Case #%d: %I64d\n", I, ans);
}
return 0;
}
坑。
坑。(据说是清华集训某题,Hash_Table A掉了,我还是太蒻了,做不了清华集训)。
坑。
题意:世界杯小组赛,每组有 4 支队伍,每两组队伍之间比赛一场。每场胜者得 3 分,负者得 0 分,平局各得 1 分。给出 4 个队伍的得分,回答能否根据该得分确定所有的比赛的胜负情况,或者回答得分不合法。
题解:签到题。暴力枚举每种胜负情况,检查每种得分是否只有一种情况对应既可。
My Code:
#include
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
int T;
int f[10][10][10][10];
int a[10], b[10], tp;
int vis[5];
int main(){
scanf("%d", &T);
int N = 729;
for(int i = 1; i <= 3; i ++){
for(int j = i + 1; j <= 4; j ++){
a[tp] = i; b[tp] = j; tp++;
}
}
for(int i = 0; i < N; i ++){
int tmp = i; memset(vis, 0, sizeof(vis));
for(int j = 0; j < 6; j ++){
if(tmp % 3 == 0){
vis[a[j]] += 3;
}else if(tmp % 3 == 1){
vis[b[j]] += 3;
}else{
vis[a[j]] += 1; vis[b[j]] += 1;
}
tmp /= 3;
}
f[vis[1]][vis[2]][vis[3]][vis[4]] ++;
}
for(int i = 1; i <= T; i ++){
int x1, x2, x3, x4;
scanf("%d%d%d%d", &x1, &x2, &x3, &x4);
printf("Case #%d: ", i);
if(x1 >= 10 || x2 >= 10 || x3 >= 10 || x4 >= 10){
printf("Wrong Scoreboard\n");
continue;
}
if(f[x1][x2][x3][x4] == 1){
printf("Yes\n");
}else if(f[x1][x2][x3][x4] == 0){
printf("Wrong Scoreboard\n");
}else{
printf("No\n");
}
}
return 0;
}