我爆零了,佛了,看来方法不太对T_T;做题太容易先入为主了,一错到底。。。。
1.light oj 1077:How Many Points? (数论gcd)
题意:给定A(x1,y1),B(x2,y2)两个点,求线段AB上有多少个点的x,y座标都是整数;
思路,一看就知道涉及到AB线段的斜率,不妨设其为k = y/x;且这是将k的最简分数,那么就意味着横座标每移动x,纵座标就会移动y,而二者都是整数,那么我们就看能在线段上移动多少次,答案是gcd(x,y)次,由于还要起点也要算,所以+1;不要忘了特判斜率为0或者不存在。
//#include<bits/stdc++.h>
#include<queue>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define mod (10007)
#define middle (l+r)>>1
#define SIZE 1000000+5
#define lowbit(x) (x&(-x))
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long ll;
typedef long double ld;
const int inf_max = 0x3f3f3f;
const ll Linf = 9e18;
const int maxn = 200+10;
const long double E = 2.7182818;
const double eps=0.0001;
using namespace std;
inline int read()
{
int f=1,res=0;
char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { res=res*10+ch-'0' ; ch=getchar(); }
return f*res;
}
ll gcd(ll a,ll b) {
return (b == 0? a : gcd(b,a%b));
}
int main()
{
int t,cas = 1;
cin>>t;
while(t--) {
ll x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
if(x1 == x2) {
printf("Case %d: %lld\n",cas++,(max(y1,y2) + 1 - min(y1,y2)));
continue;
}else if(y1 == y2) {
printf("Case %d: %lld\n",cas++,(max(x1,x2) + 1 - min(x1,x2)));
continue;
}
ll disx = abs(x1-x2),disy = abs(y1-y2);
printf("Case %d: %lld\n",cas++,gcd(disx,disy) + 1);
}
return 0;
}
2.鸣人和佐助 (计蒜客,BFS+优先队列)
题意:给一个矩阵和鸣人初始查克拉,鸣人能追上佐助的最短时间,但是有个限制:地图上有些点鸣人要花费查克拉才可以进去,没有查克拉就不能进去;
思路:我当时第一动的就是这个题(太明显了,BFS+优先队列),但是没想清楚,如果单纯地用vis数组给每个点打标记的话,假设一个点到了之后且这种情况下无法追上佐助,那么答案就不存在,因为其他点不能再走这个点;但实际上可能其他点也可能经过这个点并且能追上佐助,举个例子就是:初始查克拉为1
#@*
***
*##
**#
*#+
直接打标记就结果就是追不上,然而实际上是可以追上的。所以,我们的策略应该是:只要此时走到某个点x的查克拉大于上一次走到这个点的查克拉,那么都有可能达到我们想要的答案;
//#include<bits/stdc++.h>
#include<queue>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define mod (10007)
#define middle (l+r)>>1
#define SIZE 1000000+5
#define lowbit(x) (x&(-x))
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long ll;
typedef long double ld;
const int inf_max = 0x3f3f3f;
const ll Linf = 9e18;
const int maxn = 1000+10;
const long double E = 2.7182818;
const double eps=0.0001;
using namespace std;
inline int read()
{
int f=1,res=0;
char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { res=res*10+ch-'0' ; ch=getchar(); }
return f*res;
}
struct STATUS {
int x,y,t,ckl;
bool operator<(const STATUS &a)const {
return t > a.t;
}
}s,e;
int dx[4] = {0,1,0,-1};
int dy[4] = {1,0,-1,0};
int vis[maxn][maxn],n,m,t;
char mp[maxn][maxn];
bool check(int x,int y) {
if(x >= 1 && x <= n && y >= 1 && y <= m) return true;
return false;
}
int bfs() {
memset(vis,-1,sizeof(vis));
priority_queue<STATUS>que;
while(!que.empty()) que.pop();s.t = 0;s.ckl = t;
que.push(s);vis[s.x][s.y] = t;
while(!que.empty()) {
STATUS u = que.top();que.pop();
if(u.x == e.x && u.y == e.y) return u.t;
for(int i = 0;i < 4; ++i) {
int tx = u.x + dx[i],ty = u.y + dy[i];
if(check(tx,ty) && u.ckl > vis[tx][ty]) { //这个点的查克拉必须要大于即将走到的点的查克拉,不然对于不消耗查克拉的点就会来回跑,不断入队导致mle:
if(u.ckl == 0 && mp[tx][ty] != '#') {
STATUS tmp;tmp.x = tx;tmp.y = ty;tmp.ckl = 0;tmp.t = u.t + 1;
if(vis[tx][ty] <= tmp.ckl) {
que.push(tmp);vis[tx][ty] = tmp.ckl;
}
}else if(u.ckl) {
STATUS tmp;tmp.x = tx;tmp.y = ty;tmp.t = u.t + 1;tmp.ckl = u.ckl;
if(mp[tx][ty] == '#') --tmp.ckl;
if(vis[tx][ty] <= tmp.ckl) {
que.push(tmp);vis[tmp.x][tmp.y] = tmp.ckl;
}
}
}
}
}
return -1;
}
int main()
{
while(~scanf("%d%d%d",&n,&m,&t)) {
for(int i = 1;i <= n; ++i) {
for(int j = 1;j <= m;++j){
cin>>mp[i][j];
if(mp[i][j] == '@') {
s.x = i;s.y = j;
}
if(mp[i][j] == '+') {
e.x = i;e.y = j;
}
}
}
printf("%d\n",bfs());
}
return 0;
}
3.Censor (scu-4438) (哈希)
题意:删除给定字符串中的敏感串
思路:因为删去后,又可能形成新的敏感串,因此用栈可以快速得到删去一个敏感串后的新串。模拟一个栈,把字符串加进去,如果位于栈顶部的一部分就是敏感串就删去,字符串匹配的话就用哈希就可以。用stl的栈的话,每次删除串就只有循环删除,我写t了,但是模拟的栈就可以O(1)删除;
#include<bits/stdc++.h>
#include<queue>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define mod (1000000007)
#define middle (l+r)>>1
#define SIZE 1000000+5
#define lowbit(x) (x&(-x))
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int inf_max = 0x3f3f3f;
const ll Linf = 9e18;
const int maxn = 5e6 + 10;
const long double E = 2.7182818;
const double eps=0.0001;
using namespace std;
inline int read()
{
int f=1,res=0;
char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { res=res*10+ch-'0' ; ch=getchar(); }
return f*res;
}
string ans;
char str[maxn],word[maxn],stk[maxn];
ll _hash[maxn],f[30],whash;
const ll seed = 233;
int main()
{
//cout<<f['b']<<endl;
while(~scanf("%s%s",word,str)) {
memset(_hash,0,sizeof(_hash));
whash = 0;
ll p = 1;
int wlen = strlen(word),slen = strlen(str);
for(int i = 0;i < wlen; ++i) {
whash = (whash * seed % mod + word[i]) % mod;
p = (p * seed) % mod;
}
int top = 0;
for(int i = 0;i < slen; ++i) {
stk[++top] = str[i];
_hash[top] = (_hash[top - 1] * seed % mod + stk[top]) % mod;
while(top >= wlen) {
ll cur = (_hash[top] - p * _hash[top - wlen] % mod + mod) % mod;
if(cur == whash) top -= wlen;
else break;
}
}
for(int i = 1;i <= top;++i) {
putchar(stk[i]);
}
puts("");
}
return 0;
}
4.Aninteresting game (hdu 5975)
题意:两种操作:
求解[L,R]中所有数x的lowbit(x)之和。
给一个数x,求1~n中对于每个k,有多少个区间[k-lowbit(k) + 1,k]包含了数x
思路:
第一种操作:根据lowbit的定义,可以知道,每一个数的lowbit一定是2^k次方,那么对于[L,R]中所有数的lowbit之和也就是1,2,4,8…这些数的和,采用前缀和的思想,先求出前缀,然后相减。考虑这些1,2,4,8每个有多少个:对于一个数num,num / 2表示[1,num]中可以被2整除的数,但是其中还可能包括被4,8,16整除的,所以根据容斥原理,我们就把能被4整除的数给减去。然后计算答案即可。
第二个操作:很明显从树状数组中就可以看出,包含x的题意中的区间就是顺着树状数组想上找。
#include<bits/stdc++.h>
#include<queue>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define mod (1000000007)
#define middle (l+r)>>1
#define SIZE 1000000+5
#define lowbit(x) (x&(-x))
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int inf_max = 0x3f3f3f;
const ll Linf = 9e18;
const int maxn = 5e6 + 10;
const long double E = 2.7182818;
const double eps=0.0001;
using namespace std;
inline int read()
{
int f=1,res=0;
char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { res=res*10+ch-'0' ; ch=getchar(); }
return f*res;
}
ll n;
int q;
ll getsum(ll x){
ll ret = 0;
for(ll i = 0;(1ll << i) <= x; ++i) {
//cout<<(1ll << i)<<endl;
ll t1 = (1ll << i),t2 = (1ll << (i + 1)); //这里由于范围会超出int所以不能用int型的1来右移,会陷入死循环。
ret += (x / t1 - x / t2) * t1;
}
return ret;
}
ll getsum3(ll x){
ll ret = 0;
ll tmp = 1;
while(tmp <= x) {
ret += (x / tmp - x / (tmp << 1)) * tmp,tmp <<= 1;
}
return ret;
}
ll query(ll x){
ll ret = 0;
while(x <= n) {
++ret;
x += lowbit(x);
}
return ret;
}
int main()
{
//cout<<getsum(4294967296)<<endl;
while(~scanf("%lld%d",&n,&q)) {
while(q--) {
int t;
cin>>t;
if(t == 1) {
ll l,r;
scanf("%lld%lld",&l,&r);
printf("%lld\n",getsum(r) - getsum(l - 1));
}else if(t == 2) {
ll x;
scanf("%lld",&x);
printf("%lld\n",query(x));
}
}
}
return 0;
}
题目
给n组数,每组m个数,还有一个K(对一组数中的第i个数作用会作用于所有第i组数的第i个数),用不超过k的cost把n组数中连续的一段的每一个数都变成0,求这个连续段的最长长度。
思路:枚举左端点,二分从这个左端点开始的长度,然后查询这个区间的最大值,我是想着用线段树来维护最大值的,时间复杂度应该是O(nlognlogn)吧 ,感觉过是没有问题的,但是T了,就很离谱。。。只有换一种查询最大值的方法—rmq,这个我是真没怎么用过,查询差不多又忘了。。,rmqO(nlogn)预处理,然后O(1)查询,总的时间复杂度O(nlogn);
//#include<bits/stdc++.h>
#include<queue>
#include <cmath>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define mod (1000000007)
#define middle (l+r)>>1
#define SIZE 1000000+5
#define lowbit(x) (x&(-x))
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int inf_max = 0x3f3f3f3f;
const ll Linf = 9e18;
const int maxn = 1e5 + 10;
const long double E = 2.7182818;
const double eps=0.0001;
using namespace std;
inline int read()
{
int f=1,res=0;
char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { res=res*10+ch-'0' ; ch=getchar(); }
return f*res;
}
int n,m,k,a[maxn][10],dp[10][maxn][20];
int main()
{
while(~scanf("%d%d%d",&n,&m,&k)) {
memset(dp,0,sizeof(dp));
for(int i = 1;i <= n; ++i) {
for(int j = 1;j <= m; ++j) {
scanf("%d",&a[i][j]);
}
}
for(int i = 1;i <= m; ++i)
for(int j = 1;j <= n; ++j)
dp[i][j][0] = a[j][i];
for(int i = 1;i <= m; ++i) {
for(int tt = 1;(1 << tt) <= n; ++tt) { //由于状态的转移是从短区间来的,所以先把短区间更新了
for(int j = 1;j + (1 << tt) - 1 <= n; ++j) {
dp[i][j][tt] = max(dp[i][j][tt - 1],dp[i][j + (1 << (tt - 1))][tt - 1]);
}
}
}
int anslen = 0,ansstart,flag = 0;
for(int i = 1;i <= n; ++i) { //枚举左端点
int l = 1,r = n + 1 - i,tmpans = 0;
while(l <= r) { //枚举长度
int mid = (l + r) >> 1,cost = 0,tt = log2(mid);
int L = i,R = i + mid - 1;
for(int j = 1;j <= m; ++j) {
cost += max(dp[j][L][tt],dp[j][R - (1 << tt) + 1][tt]);
}
if(cost > k) r = mid - 1;
else {
l = mid + 1;
tmpans = mid;
}
}
if(tmpans > anslen) {
flag = 1;
anslen = tmpans;
ansstart = i;
}
}
if(!flag)
for(int i = 1;i <= m; ++i) printf("0%c",i==m?'\n':' ');
else
for(int i = 1;i <= m; ++i) {
int l = ansstart,r = ansstart + anslen - 1;
int tt = log2(anslen);
printf("%d%c",max(dp[i][l][tt],dp[i][r - (1 << tt) + 1][tt]),i == m ? '\n' : ' ');
}
}
return 0;
}