A

  只要打个表就能发现,1,6,8,9的所有排列就可以产生0~6的余数了...

  所以...走不下去的时候一定要打表...

  

 #define rep(i,n) for(int i=0 ; i<(n) ; i++)
#define mid ((l+r)>>1)
#define ls ((rt<<1))
#define rs ((rt<<1)+1)
#define maxn 1000100
char s[maxn];
string s2,f[];
int n,cnt[],m[]={,,,};
int getr(string t){
int r=;
rep(i,(int)t.size()){
int cur=t[i]-'';
r = (r*+cur)%;
}
// cout<<"string="<<t<<" r="<<r<<endl;
return r;
}
bool vis[];
void dfs(int step,string cur){
if (step==){
f[getr(cur)]=cur;
return;
}
// cout<<cur<<endl;
for (int i= ; i< ; i++ ) if (!vis[m[i]]){
vis[m[i]]=;
dfs(step+,cur+(char)(m[i]+''));
vis[m[i]]=;
}
}
int main(){
// freopen("test","r",stdin);
dfs(,(string)"");
scanf("%s",s);
n = strlen(s);
rep(i,n) cnt[s[i]-'']++;
rep(i,) cnt[m[i]]--;
s2 = "";
for (int i= ; i<= ; i++ ){
rep(j,cnt[i]) s2 += (char)(i+'');
}
int len = n-s2.size()-cnt[];
string s1 = s2;
rep(i,len) s1 += '';
int r = getr(s1);
s2 += f[(-r)%];
rep(i,cnt[]) s2 += '';
getr(s2);
cout<<s2<<endl;
return ;
}

B

  首先,想象一下,找到最大的矩形,可以看作在一些右端点对齐的线段中找到最大的矩形,然后枚举列就可以了,

  然后注意到"能重排row"这个条件,把所有线段按长度排序,扫一遍就能更新出最大,这个复杂度是 O(n*n*logn)的,

  最后,规模只有5000,排序可以用O(n)的基数排序,复杂度O(n*n).

 #include <map>
#include <ctime>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long llong;
#define rep(i,n) for(int i=0 ; i<(n) ; i++)
#define mid ((l+r)>>1)
#define ls ((rt<<1))
#define rs ((rt<<1)+1)
#define maxn 5001
int n,m,h[maxn],idx[maxn];
char g[maxn][maxn];
bool cmp(int x,int y){return x>y;}
int main(){
// freopen("test","r",stdin);
scanf("%d%d",&n,&m);
int area=;
rep(r,n) scanf("%s",g[r]);
vector<int>rcd;
rep(c,m){
// printf("add col:%d\n",c);
rep(r,n) {
h[r]=(g[r][c]=='')?h[r]+:;
// printf("g[%d][%d]=%d h[%d]=%d \n",r,c,g[r][c],r,h[r]);
}
rcd.clear();
rep(r,n) if(h[r]) rcd.push_back(h[r]);
sort(rcd.begin(),rcd.end(),cmp);
rep(i,(int)rcd.size()) area=max(area,rcd[i]*(i+));
}
printf("%d\n",area);
return ;
}

C

  本来以为是一道插头dp之类的...但是分析可以发现:

  1.特殊点<=8,可以状压;

  2.每走一步,可以确定当前轮廓中有哪些特殊点(根据题面给出的算法);

  于是可以转化为一个最短路问题:

  dist[i][j][mask], 到达(i,j)点的状态为mask时的最大价值,

  为了合法,最后答案的mask中代表'B'的位为0.

  总复杂度O(n*m*28*4*8)

 typedef long long llong;
#define rep(i,n) for(int i=0 ; i<(n) ; i++)
#define mid ((l+r)>>1)
#define ls ((rt<<1))
#define rs ((rt<<1)+1)
#define maxn 21
#define MASK (1<<8)
char g[maxn][maxn];
int n,m,id[maxn][maxn],cv,cb,var[MASK],
dr[]={,,-,},
dc[]={,,,-},
d[maxn][maxn][MASK];
bool inq[maxn][maxn][MASK];
struct node{
int r,c,msk;
};queue<node> q;
vector<node> b;
void input(){
scanf("%d%d",&n,&m);
rep(i,n) scanf("%s",g[i]);
memset(id,-,sizeof(id));
rep(i,n) rep(j,m) if (isdigit(g[i][j]))
id[i][j]=g[i][j]-'',cv++;
rep(i,n) rep(j,m) if (g[i][j]=='B')
id[i][j]=cv+(cb++);
rep(i,n) rep(j,m) {
if (g[i][j]=='B')
b.push_back((node){i,j,(<<id[i][j]>>cv)});
if (isdigit(g[i][j]))
b.push_back((node){i,j,(<<id[i][j]<<cb)});
}
rep(i,cv) scanf("%d",&var[<<i]);
}
int lowbit(int x) {return x&(-x);}
void pretreat(){
rep(i,MASK) if (i) var[i]=var[i-lowbit(i)]+var[lowbit(i)];
}
bool invalid(node cur){
if (cur.r<||cur.r>=n||cur.c<||cur.c>=m) return true;
if (id[cur.r][cur.c]!=-) return true;
if (g[cur.r][cur.c]=='#') return true;
return false;
}
void updata(node nxt,int x){
if (d[nxt.r][nxt.c][nxt.msk]==- || d[nxt.r][nxt.c][nxt.msk]>x){
d[nxt.r][nxt.c][nxt.msk]=x;
if (!inq[nxt.r][nxt.c][nxt.msk]){
inq[nxt.r][nxt.c][nxt.msk]=;
q.push(nxt);
}
}
}
int bfs(){
memset(d,-,sizeof(d));
memset(inq,,sizeof(inq));
node cur,nxt;
int ans=;
rep(i,n) rep(j,m) if (g[i][j]=='S'){
q.push((node){i,j,});
d[i][j][]=;
inq[i][j][]=;
break;
}
while (!q.empty()){
cur = q.front(); q.pop();
inq[cur.r][cur.c][cur.msk]=;
if (g[cur.r][cur.c]=='S' && ((cur.msk&((<<cb)-))==) )
ans = max(ans,var[cur.msk>>cb]-d[cur.r][cur.c][cur.msk]);
rep(i,){
nxt.r = cur.r+dr[i];
nxt.c = cur.c+dc[i];
if (invalid(nxt)) continue;
if (i&){
nxt.msk=cur.msk;
rep(j,(int)b.size()) {
if (i==) {
if (b[j].r>nxt.r && b[j].c==nxt.c) nxt.msk^=b[j].msk;
} else {
if (b[j].r>cur.r && b[j].c==cur.c) nxt.msk^=b[j].msk;
}
}
}else nxt.msk = cur.msk;
updata(nxt,d[cur.r][cur.c][cur.msk]+);
}
}
return ans;
}
int main(){
input();
pretreat();
printf("%d\n",bfs());
return ;
}

  trick:

  判断点是否在轮廓中时,只要考虑一个方向的射线;

  同一个点向相反的方向走,在mask中更新的点不一样.

D

  合并树:

  以col为关键字造平衡树,回答在u上询问时,先递归回答它所有子树的询问;

  然后合并子树,更新k值,因为每加入一个节点,k变化一次,所以均摊复杂度O(n),合并树的总复杂度O(nlogn);

  

  朴素做法:

  共用2个数组记录颜色频率feq和k值,回答u询问时,先递归回答它子树询问,

  回溯时删除之前递归产生的统计,然后递归到下一个子树...

  for x in e[u]:

    dfs(x),del(x);

  最后加入所有子树,回答u上的询问:

  for x in e[u]:

    add(x);

  这样的做法是O(n2)的.

  优化:

  最大的子树不进行del和add操作,可以优化为O(nlogn).

  分析:

  对于节点v,它被del时的祖先为u,那么size[u]>=2*size[v],所以v最多有logn个这样的祖先u,即它最多被执行longn次del操作,

  add操作比del操作多一次.

  

 #define rep(i,n) for(int i=0 ; i<(n) ; i++ )
#define ls ((rt)<<1)
#define rs (((rt)<<1)+1)
#define mid ((l+r)>>1)
#define maxn 100100
vector<int> e[maxn],q[maxn];
int qk[maxn],sz[maxn],n,m,col[maxn],cnt[maxn],f[maxn],ans[maxn]; void add(int x,int fa){
cnt[col[x]]++;
f[cnt[col[x]]]++;
rep(i,(int)e[x].size()) if(e[x][i]!=fa)
add(e[x][i],x);
}
void del(int x,int fa){
f[cnt[col[x]]]--;
cnt[col[x]]--;
rep(i,(int)e[x].size()) if(e[x][i]!=fa){
del(e[x][i],x);
}
}
void dfs(int cur,int fa){
int big=-,idx=-;
rep(i,(int)e[cur].size()) if(e[cur][i]!=fa){
if (sz[e[cur][i]]>big){
big = sz[e[cur][i]];
idx = i;
}
}
rep(i,(int)e[cur].size()) if(e[cur][i]!=fa){
if (i==idx) continue;
dfs(e[cur][i],cur);
del(e[cur][i],cur);
}
if (idx!=-) dfs(e[cur][idx],cur);
cnt[col[cur]]++;
f[cnt[col[cur]]]++;
rep(i,(int)e[cur].size()) if(e[cur][i]!=fa){
if (i==idx) continue;
add(e[cur][i],cur);
}
rep(i,(int)q[cur].size()){
int id = q[cur][i];
int k = qk[id];
ans[id] = f[k];
}
}
void initsz(int x,int fa){
sz[x]=;
rep(i,(int)e[x].size()) if(e[x][i]!=fa){
initsz(e[x][i],x);
sz[x] += sz[e[x][i]];
}
}
int main(){
// freopen("test.txt","r",stdin);
scanf("%d%d",&n,&m);
for (int i= ; i<=n ; i++ ) scanf("%d",&col[i]);
for (int i= ; i<n ; i++ ){
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
initsz(,);
rep(i,m){
int k,x;
scanf("%d%d",&x,&k);
q[x].push_back(i);
qk[i]=k;
}
dfs(,);
rep(i,m) printf("%d\n",ans[i]);
return ;
}

E

  第一步可以把问题转化为:求用不超过cntb个的黑点覆盖整个树的最小代价,在本来是黑点处放黑点代价为0,在红点处放黑点代价为1;

  然后当假设总共用了i个黑点,问题貌似就能表示为一个容量为i的背包了:

  dp[u][i] 表示u为根的子树用i个黑点覆盖的最小代价;

  不过还有一个问题:u被覆盖的情况很复杂,可能覆盖点来自u这颗子树的外面.

  于是需要再加以为来描述覆盖u的点:

  dp[u][i][w] 表示u为根的子树,用i个黑点覆盖,此外u已经被w覆盖 的最小代价.

  最后我们的答案就是 best[0][i] = min{dp[0][i][w]}  (0<i<=cntb , 0<=w<n);

  

 using namespace std;
#define rep(i,n) for(int i=0 ; i<(n) ; i++ )
#define ls ((rt)<<1)
#define rs (((rt)<<1)+1)
#define mid ((l+r)>>1)
#define maxn 501
#define INF 10001
struct node{
int v,w;
};vector<node> e[maxn];
short dp[maxn][maxn][maxn];
int dp2[maxn][maxn],best[maxn][maxn],
col[maxn],dist[maxn][maxn],n,x,sz[maxn]; void initdist(int u,int fa,int rt,int d) {
if (d>=x+) d=x+;
dist[rt][u]=d;
rep (i,(int)e[u].size()) {
if (e[u][i].v==fa) continue;
initdist(e[u][i].v,u,rt,d+e[u][i].w);
}
} void dfs(int u,int fa) {
int chd[maxn],nch=;
sz[u]=;
rep (i,(int)e[u].size()) {
if (e[u][i].v==fa) continue;
dfs(e[u][i].v,u);
chd[nch++]=e[u][i].v;
sz[u] += sz[e[u][i].v];
}
// printf("*********************************\n");
// printf("u:%d\n",u);
rep (w,n) {
if (dist[u][w]>x) continue;
if (nch) {
int tot=,c,c1;
c = chd[];
tot += sz[c];
rep (i,tot)
dp2[c][i] = min((int)dp[c][i][w],best[c][i]);
for (int i= ; i<nch- ; i++ ) {
c = chd[i];
c1 = chd[i+];
rep (j,tot+sz[c1]+) dp2[c1][j] = INF;
rep (j,tot) rep(k,sz[c1]+) {
if (k<sz[c1]) dp2[c1][j+k] = min(dp2[c1][j+k],dp2[c][j]+dp[c1][k][w]);
dp2[c1][j+k] = min(dp2[c1][j+k],dp2[c][j]+best[c1][k]);
}
tot += sz[c1];
}
}
// printf("w:%d\n",w);
// rep (i,nch) rep (j,sz[u]+1) printf("dp2[%d][%d]=%d\n",chd[i],j,dp2[chd[i]][j]);
rep (i,sz[u]) {
if (nch) dp[u][i][w] = dp2[chd[nch-]][i];
else dp[u][i][w] = ;
// printf("dp[%d][%d][%d]=%d\n",u,i,w,dp[u][i][w]);
}
rep (i,sz[u]) {
best[u][i+] = min(best[u][i+],dp[u][i][w]+(col[w]==));
}
}
// rep (i,sz[u]+1) printf("best[%d][%d]=%d\n",u,i,best[u][i]);
// printf("*********************************\n");
} void solv() {
rep (i,n) initdist(i,-,i,);
rep (i,maxn) rep (j,maxn) rep (k,maxn) dp[i][j][k]=INF;
rep (i,maxn) rep (j,maxn) dp2[i][j]=INF,best[i][j]=INF;
dfs(,-);
int ans = INF;
int cntb=;
rep (i,n) cntb += col[i];
rep (i,cntb+) ans = min(ans,best[][i]);
printf("%d\n",ans==INF?-:ans);
} int main(){
// freopen("test.txt","r",stdin);
scanf("%d%d",&n,&x);
rep (i,n) scanf("%d",&col[i]);
rep (i,n-) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
u--, v--;
e[u].push_back((node){v,w});
e[v].push_back((node){u,w});
}
solv();
return ;
}

  背包部分的复杂度:

  看起来似乎每次dfs,背包部分的复杂度是o(n^2)的,但实际整个过程中,对于每个w,每对lca恰好访问一次,所以均摊复杂度是 O(n2),总复杂度O(n3)

(p.s 最开始仿照rank1的写法dp,然后发现dp2的下标可以用直接用节点编号就不用每次dfs都初始化1次,于是后面按自己想法写...

  最后发现答案偏小了,呆看了一阵发现,枚举w的时候dp2也需要初始化...)

这套题的D,E还蛮考复杂度分析的,涨姿势了...

  

Codeforce 221 div1的更多相关文章

  1. Codeforce 222 div1

    A 假设只有一个连通块,任选一个点入队,按bfs/dfs序删除即可. trick: 要考虑有多个连通块的情况,不一定无解. #define rep(i,n) for(int i=0 ; i<(n ...

  2. Codeforce 219 div1

    B 4D"部分和"问题,相当于2D部分和的拓展,我是分解成2D部分和做的: f[x1][y1][x2][y2]=true/false 表示 左上(x1,y1) 右下(x2,y2)的 ...

  3. Codeforce 215 div1

    C 把每个qi看成点,则问题转化为:求一个最大的k,遍历k个点的完全图需要的最小步数+1不超过n, (这里+1的原因是把起点加进去) 讨论k的奇偶: k为奇数,每个点度数为偶数,这是一个欧拉回路,步数 ...

  4. css小随笔

    一.什么是CSS W3C标准中,倡导有3:其一为内容与表现分离,其二为内容与行为分离,其三为内容结构的语义化.其倡导中第一条的"表现"指的便可以说是CSS.CSS全称Cascadi ...

  5. 学起来 —— CSS 入门基础

    Hello,大家好! 小女来更博啦!CSS福利送上~~~ 首先给大家介绍一下CSS到底是什么? 一.CSS概念 W3C规范中,要求有三条:一 为"两个分离",二 为语言遵循语义化, ...

  6. ACM思维题训练 Section A

    题目地址: 选题为入门的Codeforce div2/div1的C题和D题. 题解: A:CF思维联系–CodeForces -214C (拓扑排序+思维+贪心) B:CF–思维练习-- CodeFo ...

  7. Codeforce Round #221 Div2

    每次的CF都是一把辛酸泪! 什么时候能打破这局面,昨天做着睡着了! 有时候有的题目也就差一线! 哎,! A:杠杆原理! B:算最后负的和! B:没弄出来当时就脑短路... C:事后写了个n*log(n ...

  8. Android Weekly Notes Issue #221

    Android Weekly Issue #221 September 4th, 2016 Android Weekly Issue #221 ARTICLES & TUTORIALS And ...

  9. CF#345 (Div1)

    论蒟蒻如何被cf虐 以下是身败名裂后的题解菌=========== Div1 A.Watchmen 有n个点,每个点有一个坐标.求曼哈顿距离=欧几里得距离的点对数量. 只需要统计x或y一样的点对数量. ...

随机推荐

  1. [RxJS] Creation operators: interval and timer

    It is quite common to need an Observable that ticks periodically, for instance every second or every ...

  2. [转] Gradle中的buildScript代码块

    PS: 在build script中的task apply plugin: 'spring-boot' 需要 classpath("org.springframework.boot:spri ...

  3. 单篇文章JS模拟分页

    废话部分 前两天做了一个前台分页插件,支持ajax读取数据绑定前台 和 url带页码参数跳转两种方式.于是稍加改动,做了一个单篇文章js模拟分页的代码,为什么说是模拟分页呢?因为在服务器响应HTML请 ...

  4. Android v2.0 基本概念 - 浅谈

    目录 Android框架 Linux Kernel 系统运行库 Libraries Android Runtime Application Framework Application Android框 ...

  5. 【转】Understanding and Using rem Units in CSS

    CSS units have been the subject of several articles here on SitePoint (such as A Look at Length Unit ...

  6. Swift - 36 - 结尾闭包(Trailing closure)和捕获数值(Capturing Values)的简单介绍

    //: Playground - noun: a place where people can play import UIKit // 初始化一个整数数组 var arr = [1, 3, 5, 7 ...

  7. [转] linux 下 进程和线程的区别

    1.进程与线程 进程是程序执行时的一个实例,即它是程序已经执行到课中程度的数据结构的汇集.从内核的观点看,进程的目的就是担当分配系统资源(CPU时间.内存等)的基本单位. 线程是进程的一个执行流,是C ...

  8. javascript 写一段代码,判断一个字符串中出现次数最多的字符串,并统计出现的次数

    javascript 写一段代码,判断一个字符串中出现次数最多的字符串,并统计出现的次数 function test(){ var bt = document.getElementById(" ...

  9. nodejs调试

    1.通过debug命令进行调试 node debug app.js 运行的结果: 在debug状态下输入"repl"命令可以评估变量和表达式的值 按下'CTRL+C'可以退出rep ...

  10. CWnd类

    CWnd类的成员 .数据成员 m_hWnd 指明与这个CWnd对象相关联的HWND句柄 .构造和析构 CWnd 构造一个CWnd对象 DestroyWindow 销毁相关联的Windows窗口 .初始 ...