题解之前
今天是三体的题目背景,比什么美好的每一天好理解多了。
水滴
难得的NOIP模拟题,滑窗解决。
也可以二分区间长度,再进行统计。
我的(nlogn)算法
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<ctime>
#define FN "drop"
const int maxn=2e5+5;
int dan[maxn];
int cnt[maxn];
int need[maxn];
int n,k,R;
bool check(int len) {
memset(cnt,0,sizeof cnt);
int res=R;
for(int i=1;i<=len;i++) {
cnt[dan[i]]++;
if(cnt[dan[i]]==need[dan[i]]) --res;
if(!res) return true;
}
for(int l=2,r=len+1;r<=n;l++,r++) {
--cnt[dan[l-1]];
if(cnt[dan[l-1]]==need[dan[l-1]]-1) ++res;
++cnt[dan[r]];
if(cnt[dan[r]]==need[dan[r]]) --res;
if(!res) return true;
}
return false;
}
int main() {
freopen(FN".in","r",stdin);
freopen(FN".out","w",stdout);
int T;scanf("%d",&T);
while(T--) {
memset(need,0,sizeof need);
memset(dan,0,sizeof dan);
scanf("%d%d%d",&n,&k,&R);
for(int i=1;i<=n;i++) scanf("%d",dan+i);
for(int i=1;i<=R;i++) {
int c,num;scanf("%d%d",&c,&num);
need[c]=num;
}
int l=0,r=n+1,ans=-1;
while(l<r) {
int mid=l+r>>1;
if(check(mid)) {
ans=mid;
r=mid;
}
else l=mid+1;
}
if(!~ans) printf("DESTROY ALL
");
else printf("%d
",ans);
}
return 0;
}
std的O(n)算法
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 200000;
int D[MAXN + 5], ned[MAXN + 5];
int tot[MAXN + 5], nw[MAXN + 5];
void solve() {
int N, K, R;
scanf("%d%d%d", &N, &K, &R);
for(int i=0;i<K;i++)
ned[i] = tot[i] = 0;
for(int i=1;i<=N;i++) {
scanf("%d", &D[i]);
tot[D[i]]++;
}
for(int i=1;i<=R;i++) {
int B, Q;
scanf("%d%d", &B, &Q);
ned[B] = Q;
}
for(int i=0;i<K;i++)
if( tot[i] < ned[i] ) {
puts("DESTROY ALL");
return ;
}
int ans = N, re = R, le = 1, ri = 0;
while( le <= N ) {
while( ri + 1 <= N && re ) {
nw[D[++ri]]++;
if( nw[D[ri]] == ned[D[ri]] )
re--;
}
if( !re ) ans = min(ans, ri-le+1);
if( nw[D[le]] == ned[D[le]] ) re++;
nw[D[le++]]--;
}
printf("%d
", ans);
}
int main() {
freopen("drop.in", "r", stdin);
freopen("drop.out", "w", stdout);
int T;
scanf("%d", &T);
for(int i=1;i<=T;i++)
solve();
}
“神“
2-SAT裸题?
一眼看出来2-SAT,边都连好了,写不来算法。
太菜了。
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 5000;
struct edge{
int to;
edge *nxt;
}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt=&edges[0];
void addedge(int u, int v) {
edge *p = (++ecnt);
p->to = v, p->nxt = adj[u], adj[u] = p;
}
void init() {
ecnt = &edges[0];
for(int i=0;i<=MAXN;i++)
adj[i] = NULL;
}
bool vis[MAXN + 5];
void dfs(int x) {
vis[x] = true;
for(edge *p=adj[x];p!=NULL;p=p->nxt)
if( !vis[p->to] ) dfs(p->to);
}
void solve() {
init(); int N, M;
scanf("%d%d", &N, &M);
for(int i=1;i<=M;i++) {
int u, v;
scanf("%d%d", &u, &v);
if( u < 0 )
u = (-u-1)<<1|1;
else u = (u-1)<<1;
if( v < 0 )
v = (-v-1)<<1|1;
else v = (v-1)<<1;
addedge(u, v^1);
addedge(v, u^1);
}
int ans = 3;
for(int i=0;i<N;i++) {
bool f1 = false, f2 = false, f3 = false;
for(int j=0;j<(N<<1);j++)
vis[j] = false;
dfs(i<<1);
f1 = vis[i<<1|1];
for(int j=0;j<(N<<1);j++)
vis[j] = false;
dfs(i<<1|1);
f2 = vis[i<<1];
for(int j=0;j<N;j++)
f3 = f3 || vis[j<<1];
if( f1 && f2 ) ans = min(ans, 0);
else if( f2 ) ans = min(ans, 1);
else if( f3 ) {
if( f1 ) ans = min(ans, 1);
else ans = min(ans, 2);
}
}
if( ans == 3 ) puts("No Way");
else printf("%d
", ans);
}
int main() {
freopen("god.in", "r", stdin);
freopen("god.out", "w", stdout);
int T;
scanf("%d", &T);
for(int i=1;i<=T;i++)
solve();
}
执剑人
贪心+数据结构。
只会贪心,于是只有60分。
用后缀和来选出枪毙名单。
用栈存从一边开始的枪毙名单,线段树从另外一边开始操作。
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int INF = (1<<30);
const int MAXN = 500000;
struct query{
int ri, ind;
query(int _r, int _i):ri(_r), ind(_i){}
};
vector<query>qry[MAXN + 5];
char str[MAXN + 5];
int stk[MAXN + 5], ans[MAXN + 5], top;
struct node{
int mss, sum;
int le, ri;
}tree[4*MAXN + 5];
void PushUp(int x) {
tree[x].mss = min(tree[x<<1|1].mss, tree[x<<1].mss + tree[x<<1|1].sum);
tree[x].sum = tree[x<<1].sum + tree[x<<1|1].sum;
}
void Build(int x, int l, int r) {
tree[x].le = l, tree[x].ri = r;
if( l == r ) {
tree[x].mss = tree[x].sum = 0;
return ;
}
int mid = (l + r) >> 1;
Build(x<<1, l, mid);
Build(x<<1|1, mid+1, r);
PushUp(x);
}
void Modify(int x, int pos, int key) {
if( pos > tree[x].ri || pos < tree[x].le )
return ;
if( tree[x].le == tree[x].ri ) {
tree[x].sum = tree[x].mss = key;
return ;
}
Modify(x<<1, pos, key);
Modify(x<<1|1, pos, key);
PushUp(x);
}
node Query(int x, int pos) {
if( tree[x].ri <= pos )
return tree[x];
int mid = (tree[x].le + tree[x].ri) >> 1;
if( pos <= mid )
return Query(x<<1, pos);
else {
node ret = Query(x<<1|1, pos);
ret.mss = min(ret.mss, ret.sum + tree[x<<1].mss);
ret.sum = ret.sum + tree[x<<1].sum;
return ret;
}
}
inline int Read() {
char ch = getchar(); int x = 0, f = 1;
while( (ch > '9' || ch < '0') && ch != '-' ) ch = getchar();
if( ch == '-' ) f = -1, ch = getchar();
while( '0' <= ch && ch <= '9' )
x = 10*x + ch-'0', ch = getchar();
return x * f;
}
int main() {
freopen("sworder.in", "r", stdin);
freopen("sworder.out", "w", stdout);
int N, Q;
scanf("%d%s%d", &N, str+1, &Q);
for(int i=1;i<=Q;i++) {
int l, r;
l = Read(), r = Read();
qry[l].push_back(query(r, i));
}
Build(1, 1, N); top = N+1;
for(int i=N;i>=1;i--) {
if( str[i] == 'C' )
stk[--top] = i;
else {
if( top != N+1 ) {
Modify(1, stk[top], -1);
stk[top++] = 0;
}
Modify(1, i, 1);
}
for(int j=0;j<qry[i].size();j++) {
int p = upper_bound(stk+top, stk+N+1, qry[i][j].ri) - stk;
ans[qry[i][j].ind] = (p-top) - min(0, Query(1, qry[i][j].ri).mss);
}
}
for(int i=1;i<=Q;i++)
printf("%d
", ans[i]);
}
于是今天愉快地拿到了大众分数。