比较好想的一道题,直接用队列滑窗,因为扫一遍往队列里加东西时,改变的只有一个值,开桶储存好就行了!

#include<bits/stdc++.h>
using namespace std; int n, k, r; inline int min(int a, int b) {
return a > b ? b : a;
} inline int max(int a, int b) {
return a > b ? a : b;
} int sum[], q[], cnt[], vis[], ned[];
int a[]; int main() {
freopen("drop.in", "r", stdin);
freopen("drop.out", "w", stdout);
int T;
scanf("%d", &T);
while(T --) {
memset(sum, , sizeof(sum));
memset(q, , sizeof(q));
memset(cnt, , sizeof(cnt));
memset(vis, , sizeof(vis));
memset(ned, , sizeof(ned));
scanf("%d%d%d", &n, &k, &r);
int tot = , fl = ;
for(int i = ; i <= n; i ++) {
scanf("%d", &a[i]);
sum[a[i]] ++;
}
for(int i = ; i <= r; i ++) {
int b, p;
scanf("%d%d", &b, &p);
if(!vis[b]) vis[b] = , tot ++;
if(sum[b] < p) {
fl = ; break;
}
ned[b] = max(ned[b], p);
}
if(fl) {
printf("DESTROY ALL\n"); continue;
}
int ans = 0x3f3f3f3f;
int h = , t = , now = ;
for(int i = ; i <= n; i ++) {
if(vis[a[i]]) {
q[++t] = i; cnt[a[i]] ++; if(cnt[a[i]] == ned[a[i]]) now ++;
}
while(now == tot && h < t) {
ans = min(ans, q[t] - q[h+] + );
cnt[a[q[h+]]] --;
if(cnt[a[q[h+]]] < ned[a[q[h+]]]) now --;
h ++;
}
}
printf("%d\n", ans);
}
return ;
}

考场上想到$2-sat$但是忘得差不多了,打死都理不清楚关系。

这道题算是$2-sat$板子题了,主要是如何判断的思想。

首先题目条件疯狂暗示,但是和$2-sat$的一般理解方式不同。题目上给的约束条件我们按$2-sat$让他们避免相连,实际上就是题目中的防守文明,我们要摧毁文明,实际上是要让防守文明失效。即至少有一个点和它的负点相互相通。因为$n$很小就可以用两次$dfs$实现,然后就是分类讨论来判断该加几条边。

1、假如初始连边中就有某一个点对正负点相互连接,答案就是0.

2、假如一个点对中正点连向负点:

   1)如果负点可以连向另一点对的正点,根据$2-sat$的对称行,另一点对的负点一定可以连向当前点对的正点,所以我们只用加条件$(i,j)$,就是在$-i$和$-j$之间连了一条边,使i点对相互连通。

   2)如果负点没有连向任意点对的正点,那么一定无解,输出$No Way$。

3、假如一个点对中负点连向正点:

那么只用加一个条件$(i,i)$,相当于在$i$和$-i$间连边即可。

4、假如点对之间没有连边:

1)将2、3结合,但是必须满足2的条件,这样加两条边即可。

2)没有2的条件,就无法加边,输出$No Way$。

#include<bits/stdc++.h>
using namespace std; struct Node {
int u, v, nex;
Node(int u = , int v = , int nex = ) :
u(u), v(v), nex(nex) { }
} Edge[]; int h[], stot;
void add(int u, int v) {
Edge[++stot] = Node(u, v, h[u]);
h[u] = stot;
} int vis[];
void dfs(int u) {
vis[u] = ;
for(int i = h[u]; i; i = Edge[i].nex) {
int v = Edge[i].v;
if(vis[v]) continue;
dfs(v);
}
} int n, m;
int main() {
freopen("god.in", "r", stdin);
freopen("god.out", "w", stdout);
int T;
scanf("%d", &T);
while(T --) {
memset(h, , sizeof(h));
stot = ;
scanf("%d%d", &n, &m);
for(int i = ; i <= m ; i++) {
int x, y;
scanf("%d%d", &x, &y);
if(x > || y < ) swap(x, y);
if(x > && y > ) {
add(x, y + n);
add(y, x + n);
}
if(x < && y < ) {
add(-x + n, -y);
add(-y + n, -x);
}
if(x < && y > ) {
add(-x + n, y + n);
add(y, -x);
}
}
int tag1 = , tag2 = , tag0 = ;
for(int i = ; i <= n; i ++) {
memset(vis, , sizeof(vis));
dfs(i);
int fl = ;
if(vis[i + n]) {
memset(vis, , sizeof(vis));
dfs(i + n);
fl = ;
if(vis[i]) {
tag0 = ; break;
}
for(int j = ; j <= n; j ++) {
if(j != i && vis[j]) {
tag1 = ; break;
}
}
}
memset(vis, , sizeof(vis));
dfs(i + n);
if(vis[i]) {
tag1 = ;
} else if(!fl) {
for(int j = ; j <= n; j ++) {
if(j != i && vis[j]) {
tag2 = ; break;
}
}
}
}
if(tag0) printf("0\n");
else if(tag1) printf("1\n");
else if(tag2) printf("2\n");
else printf("No Way\n");
} return ;
}

一道好题。(但是现在不想写题解了aaaa)

所以复制题解~

60分:

考虑这样一个贪心:
先从左往右扫,如果某一时刻不满足要求,则需要删除前面中某一个支持对方的人。我们贪心地选择删除当前时刻访问的人(肯定是支持对方),然后继续往后扫。
然后再从右往左扫,作相同的操作。
直观地理解是这样的:我们尽量删除靠右的人,使得从右往左扫时少删除一些人。
可以采用交换论证法证明这贪心是对的。

80-100:

首先我们可以发现从左往右扫完,从右往左扫的这个过程可以不用实现出来。只需要求出右端点开始的最小后缀和的相反数即可。
然后我们发现,如果两个询问区间拥有相同的左端点,则只需要作一次从左往右扫的工作。这使我们想到要离线化解决问题。

我们将询问按左端点排序,按照左端点从大到小的顺序求解询问。
如果已知从 i 开始向右扫需要删除那些结点,则从 i-1 开始向右扫需要删除那些结点可以快速求出。具体来说,如果i-1是支持者,则左数第一个被删除的结点与它抵消;如果i-1是反对者,则加入被删除的结点里。
该过程可以用栈维护。

通过在栈里面二分,我们可以知道区间[l, r]在从左往右扫时需要删除的结点数量。
现在问题就是求解以 r 为端点的最小后缀和。
这个东西可以用块状数组O(sqrt(N))维护(这就是80%算法的由来),更好的方法应该是用线段树O(log(N))维护
于是该题就在O((N+Q)logN)的时间复杂度内解决了。

#include<bits/stdc++.h>
using namespace std; struct Tree {
int mi, sum;
} TR[*]; int n, m;
char s[]; void update(int nd) {
TR[nd].mi = min(TR[nd << | ].mi, TR[nd << ].mi + TR[nd << | ].sum);
TR[nd].sum = TR[nd << ].sum + TR[nd << | ].sum;
} void build(int nd, int l, int r) {
if(l == r) {
TR[nd].mi = TR[nd].sum = ;
return ;
}
int mid = (l + r) >> ;
build(nd << , l, mid);
build(nd << , mid + , r);
update(nd);
} void modify(int nd, int l, int r, int pos, int d) {
if(l == r) {
TR[nd].sum = d;
TR[nd].mi = d;
return ;
}
int mid = (l + r) >> ;
if(pos <= mid) modify(nd << , l, mid, pos, d);
else modify(nd << | , mid + , r, pos, d);
update(nd);
} Tree query(int nd, int l, int r, int pos) {
if(l == r) return TR[nd];
int mid = (l + r) >> ;
if(pos <= mid) return query(nd << , l, mid, pos);
else {
Tree res = query(nd << | , mid + , r, pos);
res.mi = min(res.mi, res.sum + TR[nd << ].mi);
res.sum = res.sum + TR[nd << ].sum;
return res;
}
} vector < pair < int , int > > qus[];
int tp = , stk[], ans[]; int find(int pos) {
int l = , r = tp, an = tp + ;
while(l <= r) {
int mid = (l + r) >> ;
if(stk[mid] > pos) l = mid + ;
else an = mid, r = mid - ;
}
return an;
} int main() {
freopen("sworder.in", "r", stdin);
freopen("sworder.out", "w", stdout);
scanf("%d", &n);
scanf("%s", s + );
scanf("%d", &m);
for(int i = ; i <= m; i ++) {
int l, r;
scanf("%d%d", &l, &r);
qus[l].push_back(make_pair(r, i));
}
build(, , n);
for(int i = n; i >= ; i --) {
if(s[i] == 'C') stk[++tp] = i;
else {
if(tp) {
modify(, , n, stk[tp], -);
stk[tp--] = ;
}
modify(, , n, i, );
}
for(int j = ; j < qus[i].size(); j ++) {
int pos = qus[i][j].first;
int tmp = find(pos);
ans[qus[i][j].second] = (tp - tmp + ) - min(, query(, , n, pos).mi);
}
}
for(int i = ; i <= m; i ++) printf("%d\n", ans[i]);
return ;
}

【10.7校内测试】【队列滑窗】【2-sat】【贪心+栈二分+线段树(noip模拟好题)】【生日祭!】的更多相关文章

  1. 【10.11校内测试】【优先队列(反悔贪心)】【莫队】【stl的应用??离线处理+二分】

    上次做过类似的题,原来这道还要简单些?? 上次那道题是每天可以同时买进卖出,所以用两个优先队列,一个存买进,一个存卖出(供反悔的队列). 这道题实际上用一个就够了???但是不好理解!! 所以我还是用了 ...

  2. [CSP-S模拟测试]:platform(后缀数组+二分+线段树)

    题目传送门 题目描述 走过奈何桥有一个名叫望乡台的土台,望乡台有个名曰孟婆的老妇人在卖孟婆汤.一生爱恨情仇,一世浮沉得失,都可以随这碗孟婆汤遗忘得干干净净.现在有$n$碗孟婆汤摆成一排,汤的品种不超过 ...

  3. 【10.17校内测试】【二进制数位DP】【博弈论/预处理】【玄学(?)DP】

    Solution 几乎是秒想到的水题叻! 异或很容易想到每一位单独做贡献,所以我们需要统计的是区间内每一位上做的贡献,就是统计区间内每一位是1的数的数量. 所以就写数位dp辣!(昨天才做了数字统计不要 ...

  4. 【10.5校内测试】【DP】【概率】

    转移都很明显的一道DP题.按照不优化的思路,定义状态$dp[i][j][0/1]$表示吃到第$i$天,当前胃容量为$j$,前一天吃(1)或不吃(0)时能够得到的最大价值. 因为有一个两天不吃可以复原容 ...

  5. 【10.31校内测试】【组合数学】【记忆化搜索/DP】【多起点多终点二进制拆位Spfa】

    Solution 注意取模!!! Code #include<bits/stdc++.h> #define mod 1000000007 #define LL long long usin ...

  6. 【10.29校内测试】【线段树】【DP】【二进制Trie树求最小值最大】

    Solution 标程太暴力惹QAQ 相当于是26棵线段树的说QAQ 不过我写了另一种写法,从大到小枚举每一个字母,标记字典序在这个字母之上的位置为1,每次都建一棵线段树,维护1的数量,即区间和. 修 ...

  7. 【10.26校内测试】【状压?DP】【最小生成树?搜索?】

    Solution 据说正解DP30行??? 然后写了100行的状压DP?? 疯狂特判,一算极限时间复杂度过不了aaa!! 然而还是过了....QAQ 所以我定的状态是待转移的位置的前三位,用6位二进制 ...

  8. 【10.6校内测试】【小模拟】【hash+线段树维护覆盖序列】

    一开始看到题就果断跳到T2了!!没想到T2才是个大坑,浪费了两个小时QAQ!! 就是一道小模拟,它怎么说就怎么走就好了! 为什么要用这么多感叹号!!因为统计答案要边走边统计!!如果每个数据都扫一遍20 ...

  9. 【10.4校内测试】【轮廓线DP】【中国剩余定理】【Trie树+博弈】

    考场上几乎是一看就看出来轮廓线叻...可是调了两个小时打死也过不了手出样例!std发下来一对,特判对的啊,转移对的啊,$dp$数组竟然没有取max!!! 某位考生当场死亡. 结果下午又请了诸位dala ...

随机推荐

  1. Spring4笔记3--Bean的装配

    Bean的装配: Bean 的装配,即 Bean 对象的创建.容器根据代码要求创建 Bean 对象后再传递给代码的过程,称为 Bean 的装配. 1. 创建Bean对象的方式: 1. 通过 getBe ...

  2. Hibernate5笔记5--关联关系映射

    关联关系映射: 关联关系,是使用最多的一种关系,非常重要.在内存中反映为实体关系,映射到DB中为主外键关系.实体间的关联,即对外键的维护.关联关系的发生,即对外键数据的改变. 外键:外面的主键,即,使 ...

  3. 【原创】Linux环境下的图形系统和AMD R600显卡编程(1)——Linux环境下的图形系统简介

    Linux/Unix环境下最早的图形系统是Xorg图形系统,Xorg图形系统通过扩展的方式以适应显卡和桌面图形发展的需要,然而随着软硬件的发展,特别是嵌入式系统的发展,Xorg显得庞大而落后.开源社区 ...

  4. Prepare tasks for django project deployment.md

    As we know, there are some boring tasks while deploy Django project, like create db, do migrations a ...

  5. ExtJs的Reader

    ExtJs的Reader Reader : 主要用于将proxy数据代理读取的数据按照不同的规则进行解析,讲解析好的数据保存到Modle中 结构图 Ext.data.reader.Reader 读取器 ...

  6. java基础67 JavaScript通过关系找节点、添加附件(网页知识)

    1.通过关系找节点(父子关系,兄弟关系) 1.1.常用方法 parentNode:获取当前元素的父节点.    childNodes:获取当前元素的所有下一级子元素    firstChild:获取当 ...

  7. No.17 selenium学习之路之判断与等待

    一.三种等待方式 1.sleep 加载time库.time.sleep() 休眠单位以秒为单位 2.implicitly_wait() 等待页面完全加载完成(左上角转圈结束) 参数为等待时间,等待页面 ...

  8. 修饰符(动态String数组篇)--- 常用 解除疑问。

    1.无修饰符----是直接传基本类型的地址过来,并没有把基本类型的指针复制一份入栈,所以一旦修改就是修改原来的值. 2.const 修饰符 与 无修饰符一致. 3.var修饰符 与 上一致. 4.ou ...

  9. 区间DP小结

    也写了好几天的区间DP了,这里稍微总结一下(感觉还是不怎么会啊!). 但是多多少少也有了点感悟: 一.在有了一点思路之后,一定要先确定好dp数组的含义,不要模糊不清地就去写状态转移方程. 二.还么想好 ...

  10. Centos之字符串搜索命令grep

    grep [选项] 字符串 文件名 在文件当中匹配符合条件的字符串 选项: -i 忽略大小写 -v 排除指定字符串 [root@localhost ~]# grep "work" ...