A题 http://codeforces.com/problemset/problem/797/A
因子分解即可,三分钟写完wa了半个小时,代码写的搓。
以此输出0.....k-2。k-1==n/已经输出的积(特判一下k==1的情况)。
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=200100;
struct node
{
int a;
int b;
}vis[maxn];
int main()
{
memset(vis,0,sizeof(vis));
int n,k;
scanf("%d%d",&n,&k);
int t=1; int sum=0; int y=n;
int cnt=0;
while(n>1)
{
++t; if(n%t) continue;
int flag=0;
while(n%t==0)
{
n=n/t; flag++;
sum++;
}
vis[cnt].a=t; vis[cnt].b=flag; cnt++;
}
if(sum<k) { printf("-1\n"); return 0;}
else if(sum==k)
{
printf("%d",vis[0].a);
vis[0].b-=1;
for(int j=0;j<cnt;j++)
{
while(vis[j].b!=0)
{
printf(" %d",vis[j].a); vis[j].b--;
}
}
printf("\n");
}
else
{
if(k==1)
{
printf("%d\n",y); return 0;
}
int ans=1; int t=0;
printf("%d",vis[0].a);
vis[0].b--; t++; ans*=vis[0].a;
for(int j=0;j<cnt;j++)
{
if(t==k-1) break;
while(vis[j].b!=0)
{
printf(" %d",vis[j].a); t++; ans*=vis[j].a; vis[j].b--;
if(t==k-1) break;
}
if(t==k-1) break;
}
if(t==k-1) {printf(" %d\n",y/ans);}
else printf("\n");
}
return 0;
}
B http://codeforces.com/problemset/problem/797/B
把所有正整数(x)加起来(同时如果x是奇数,把-x扔到优先队列中)(别忘了有些数字开始就是负奇数也要扔到队列里),如果是奇数直接输出,偶数弹出优先队列的队列首(从大到小)相加,输出即可!
#include <iostream>
#include<stdio.h>
#include<algorithm>
#include<queue>
#include<string.h>
using namespace std;
const int maxn=1e5+10;
priority_queue<int> fj,fo;
typedef long long ll;
int main()
{
int n;
scanf("%d",&n);
ll ans=0;
for(int i=0;i<n;i++)
{
int x;
scanf("%d",&x);
if(x>0)
{
ans+=x;
if(x%2) fj.push(-x);
else fo.push(-x);
}
else
{
if(x%2) fj.push(x);
else fo.push(x);
}
}
if(ans%2) printf("%I64d\n",ans);
else
{
printf("%I64d\n",ans+fj.top());
}
return 0;
}
题意看了半天,就是两种操作
1. 把串s的首字母扔到t中
2. 把串t中的最后一个字母扔到u中 (不难发现串t其实就是一个栈)
aim:使u的字典序最小
做法如下
1 首先扫一遍s串把每个字母在s串中的出现次数算出来(vis数组)
2 再扫一遍
2-1.如果栈t为空把s[i]塞入栈 ,vis[s[i]-'a']--(s串中这个字母被取出);
2-2.如果栈不为空
1.此时s串中没有比栈顶的元素更小的(0(1)在vis数组找一遍),弹出栈顶,加入到u串中。
2.如果有比栈顶的更小的,把s[i]塞入栈 ,vis[s[i]-'a']--(s串中这个字母被取出);
3别忘了,当i到n的时候 栈有可能里面还有元素,以此加入u串中
4输出
#include <iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<stack>
using namespace std;
const int maxn=1e5+120;
char s[maxn];
stack<char> mys;
char ans[maxn];
int vis[30];
int main()
{
memset(vis,0,sizeof(vis));
memset(ans,0,sizeof(ans));
scanf("%s",s);
int n=strlen(s); int cnt=0;
int i=0;
for(int i=0;i<n;i++)
{
vis[s[i]-'a']++;
}
i=0;
while(i<n)
{
if(mys.size()==0)
{
mys.push(s[i]); vis[s[i]-'a']--; i++;
}
else
{
int t=mys.top()-'a';
bool tt=0;
for(int j=0;j<t;j++)
{
if(vis[j]) tt=1;
}
if(tt)
{
mys.push(s[i]); vis[s[i]-'a']--; i++;
}
else
{
ans[cnt++]=mys.top(); mys.pop();
}
}
}
while(mys.size())
{
ans[cnt++]=mys.top(); mys.pop();
}
for(int i=0;i<cnt;i++)
{
printf("%c",ans[i]);
}
printf("\n");
return 0;
}
D http://codeforces.com/problemset/problem/797/D
英语渣题意迷了好久,意思就是有1-n个树的节点 (V-LSON-RSON,其中v是data值,LSON,RSON这里都是节点的下标)。这些节点会构成一个二叉搜索树(左儿子小于根,右儿子大于根)。
aim:有多少个节点存放不合适?
(注意:有的V会出现多次)
思路,
1.首先用vis[]数组把出现左右儿子的节点下标都记录一下了。再扫一遍1-n,没有做过标记的就是root。
2.开一个map<int,int>记录下来每个v出现多少次(以后算答案)
3 DFS(now,p,q) p是下界 q是上界;
DFS(root,-inf,inf) 从root开始依次找不合法的节点(这里用将合法的节点出现次数改为0来实现)
左儿子DFS(root.lson,p,min(q,now.data)) 因为左儿子要比根小
右儿子DFS(root.rson,max(p,now.data),q) 因为右儿子要比根大
(记得判断左右儿子是否存在)
4 做完DFS之后扫一遍树的节点如果map不为0,加到ans中。
(记得把map再改为0,因为后面有可能还有V避免重复)
5 输出
#include <iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<map>
using namespace std;
const int maxn=2e5+20;
const int inf=1e9+10;
struct node
{
int data;
int lson,rson;
}tree[maxn];//记录树节点的样子
bool vis[maxn];//判断那个节点的标号出现过,没有出现过的就是root
map<int,int> myp;//记录每个节点的值出现的次数,可能一个data出现多次!
void dfs(int now,int mi,int ma )
{
if(now==-1) return ;
if(tree[now].data>=mi&&tree[now].data<=ma)
{
if(myp[tree[now].data]) myp[tree[now].data]=0;
}
dfs(tree[now].lson,mi,min(ma,tree[now].data));
dfs(tree[now].rson,max(mi,tree[now].data),ma);
}
int main()
{
myp.clear();
memset(vis,0,sizeof(vis));
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&tree[i].data,&tree[i].lson,&tree[i].rson);
if(tree[i].lson!=-1) vis[tree[i].lson]=1;
if(tree[i].rson!=-1) vis[tree[i].rson]=1;
myp[tree[i].data]++;
}
int root;
for(int i=1;i<=n;i++) if(!vis[i]) root=i;
dfs(root,-inf,inf);
int ans=0;
for(int i=1;i<=n;i++)
{
ans+=myp[tree[i].data];
myp[tree[i].data]=0; //这个很关键防止再次加上这个节点值的出现次数
}
printf("%d\n",ans);
return 0;
}
E http://codeforces.com/problemset/problem/797/E
我的做法就是暴力模拟(当然是TLE)
看了网上的题解,大致就是
本题的入手点在于分别考虑解决问题的难点,以及巧妙地暴力。
- 在不考虑 n 的数据规模的情况下,可以根据每个查询暴力解出答案(用变量的变化模拟 p 的移动)。
- 在不考虑 k 的数据规模的情况下,可以进行动态规划。令 d[i][j] 表示当前起始位置为 i , k 为 j 的情况下需要多少步能够走出数组,那么按照 i从大到小的顺序 DP 就能得到答案 d[p][k] 。
-
但是本题的问题在于 n 和 k 的数据规模必须同时被考虑。在这种情况下,当 k 比较小的时候(比如小于 100 的时候)我们可以动态规划,当 k 比较大的时候,我们可以暴力。
(大致就是k小的时候DP)
dp[j][i] j表示k ,i表示p{
=dp[j][i+a[i]+j]+1 (当i+a[i]+j<=n时候)
=1 (当i+a[i]+j>n时候)
}
代码如下
#include <iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
const int maxn=1e5+10;
const int s=330;
int a[maxn];
int dp [s+5][maxn];
int n,k,p,q;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
memset(dp,0,sizeof(dp));
scanf("%d",&q);
for(int j=1;j<=s;j++) //j 代表的是k
{
for(int i=n;i>=1;i--) // i是p
{
if(i+j+a[i]>n) dp[j][i]=1;
else dp[j][i]=dp[j][i+a[i]+j]+1;
}
}
while(q--)
{
int p,k;
scanf("%d%d",&p,&k);
if(k<=s)
{
printf("%d\n",dp[k][p]);
continue;
}
else
{
int cnt=0;
while(p<=n)
{
cnt++;
p=p+a[p]+k;
}
printf("%d\n",cnt);
}
}
return 0;
}
F题 看了题解 用大佬们单调队列做+dp做的
本题的入手点在于恰当地选择解决问题的顺序,能设计动态规划算法并使用数据结构进行优化。
在不考虑任何数据规模的情况下,我们显然可以对每只老鼠考虑它们在哪个洞中,或对每个洞考虑它收容哪些老鼠。如果用那么可以用搜索将问题顺利解决。
但是显然我们不能不考虑数据规模,在本题的数据规模下,我们或许能够利用以上两种顺序中的一个,通过动态规划加以解决。为了让问题更清晰,我们似乎应该对老鼠和老鼠洞按坐标排序。
考虑让 d[i][j] 表示安排了前 i 个老鼠洞,前 j 只老鼠都得到妥善安置的最小的距离和。那么状态 (i,j) 由 (i−1,j),(i−1,j−1),……,(i−1,j−c[i]) 中 d 值最小的状态转移过来(其中 c[i] 表示第 i 个老鼠洞的容量)。于是在动态规划的同时做 RMQ 即可。
在思考实现方式的过程中要注意以下几个问题:
- 如何用数据结构优化 RMQ (单调队列)?
- 如何快速得知前 j 个老鼠到第 i 个洞的距离之和(预处理)?
- 如何优化算法的空间复杂度(滚动数组)?
- 累加数据会不会出问题(防溢出)?
但我RMQ只会用个线段树 最多来个ST
链接:http://blog.csdn.net/liang5630/article/details/7917702
我还是太弱了!!!!!