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. 支持向量机通俗导论(理解SVM的三层境地)

    支持向量机通俗导论(理解SVM的三层境地) 作者:July :致谢:pluskid.白石.JerryLead.出处:结构之法算法之道blog. 前言 动笔写这个支持向量机(support vector ...

  2. [转] linux下的僵尸进程处理SIGCHLD信号

    什么是僵尸进程? 首先内核会释放终止进程(调用了exit系统调用)所使用的所有存储区,关闭所有打开的文件等,但内核为每一个终止子进程保存了一定量的信息.这些 信息至少包括进程ID,进程的终止状态,以及 ...

  3. Android Studio导入GitHub上的项目常见问题(以图片轮播开源项目为实例)

    前言:github对开发者而言无疑是个宝藏,但想利用它可不是件简单的事,用Android studio导入开源项目会遇到各种问题,今天我就以github上的一个图片轮播项目为例,解决导入过程中的常见问 ...

  4. 全球最流行的66款App的共同规律

    根据苹果AppStore.Google Play.App Annie.亚马逊 AppStore及Windows Phone 应用商店历年的公开数据统计,以下66个非游戏类应用正在全球范围内流行,持续时 ...

  5. 趣味算法——青蛙过河(JAVA)

    青蛙过河是一个非常有趣的智力游戏,其大意如下: 一条河之间有若干个石块间隔,有两队青蛙在过河,每队有3只青蛙,这些青蛙只能向前移动,不能向后移动,且一次只能有一只青蛙向前移动.在移动过程中,青蛙可以向 ...

  6. grep和正则表达式

    正则表达式示例表字 符 意 义 示 例* 任意长度的字符串. a* 表示: 空字符串.aaaa.a…? 长度为0或者1的字符串. a? 表示: 空字符串和a.+ 长度为一个或者多个的字符串. a+表示 ...

  7. gc内存回收机制

    判断哪些对象可回收 GC是通过对象是否存活来决定是否进行回收,判断对象是否存活主要有两种算法:引用计数算法.可达性分析算法 引用计数算法 引用计数的算法原理是给对象添加一个引用计数器,每被引用一次计数 ...

  8. 关于Aspose对于Word操作的一些扩展及思考

    Aspose.word Aspose.Words是一款先进的类库,通过它可以直接在各个应用程序中执行各种文档处理任务.Aspose.Words支持DOC,OOXML,RTF,HTML,OpenDocu ...

  9. PHP Cookies

    PHP Cookies cookie 常用于识别用户. Cookie 是什么? cookie 常用于识别用户.cookie 是一种服务器留在用户计算机上的小文件.每当同一台计算机通过浏览器请求页面时, ...

  10. uva 10077 - The Stern-Brocot Number System

    想法: 初始化三個數L=0/1, M=1/1, R=1/0,設輸入的分數為a: 如果a<M,那麼要往左邊走,    R = M;    M = (L分子+M分子)/(L分母+M分母); 如果a& ...